@dooboostore/simple-web-component 1.0.28 → 1.0.30

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 (94) hide show
  1. package/README.md +437 -25
  2. package/dist/cjs/decorators/attribute.js +2 -1
  3. package/dist/cjs/decorators/attribute.js.map +2 -2
  4. package/dist/cjs/decorators/elementDefine.js +8 -2
  5. package/dist/cjs/decorators/elementDefine.js.map +2 -2
  6. package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
  7. package/dist/cjs/decorators/index.js +1 -0
  8. package/dist/cjs/decorators/index.js.map +2 -2
  9. package/dist/cjs/decorators/lifecycles.js +4 -0
  10. package/dist/cjs/decorators/lifecycles.js.map +2 -2
  11. package/dist/cjs/decorators/setProperty.js +97 -0
  12. package/dist/cjs/decorators/setProperty.js.map +7 -0
  13. package/dist/cjs/elements/SwcAppEngine.js +3 -3
  14. package/dist/cjs/elements/SwcAppEngine.js.map +2 -2
  15. package/dist/cjs/elements/SwcAppMixin.js +44 -6
  16. package/dist/cjs/elements/SwcAppMixin.js.map +3 -3
  17. package/dist/cjs/elements/SwcAsync.js +4 -1
  18. package/dist/cjs/elements/SwcAsync.js.map +2 -2
  19. package/dist/cjs/elements/SwcChoose.js +54 -38
  20. package/dist/cjs/elements/SwcChoose.js.map +2 -2
  21. package/dist/cjs/elements/register.js +131 -39
  22. package/dist/cjs/elements/register.js.map +2 -2
  23. package/dist/cjs/index.js +2 -1
  24. package/dist/cjs/index.js.map +2 -2
  25. package/dist/cjs/types.js.map +1 -1
  26. package/dist/cjs/utils/Utils.js +30 -1
  27. package/dist/cjs/utils/Utils.js.map +2 -2
  28. package/dist/esm/decorators/attribute.js +2 -1
  29. package/dist/esm/decorators/attribute.js.map +2 -2
  30. package/dist/esm/decorators/elementDefine.js +8 -2
  31. package/dist/esm/decorators/elementDefine.js.map +2 -2
  32. package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
  33. package/dist/esm/decorators/index.js +1 -0
  34. package/dist/esm/decorators/index.js.map +2 -2
  35. package/dist/esm/decorators/lifecycles.js +4 -0
  36. package/dist/esm/decorators/lifecycles.js.map +2 -2
  37. package/dist/esm/decorators/setProperty.js +78 -0
  38. package/dist/esm/decorators/setProperty.js.map +7 -0
  39. package/dist/esm/elements/SwcAppEngine.js +3 -3
  40. package/dist/esm/elements/SwcAppEngine.js.map +2 -2
  41. package/dist/esm/elements/SwcAppMixin.js +44 -6
  42. package/dist/esm/elements/SwcAppMixin.js.map +3 -3
  43. package/dist/esm/elements/SwcAsync.js +4 -1
  44. package/dist/esm/elements/SwcAsync.js.map +2 -2
  45. package/dist/esm/elements/SwcChoose.js +54 -38
  46. package/dist/esm/elements/SwcChoose.js.map +2 -2
  47. package/dist/esm/elements/register.js +131 -39
  48. package/dist/esm/elements/register.js.map +2 -2
  49. package/dist/esm/index.js +2 -1
  50. package/dist/esm/index.js.map +2 -2
  51. package/dist/esm/types.js.map +1 -1
  52. package/dist/esm/utils/Utils.js +30 -1
  53. package/dist/esm/utils/Utils.js.map +2 -2
  54. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +118 -4
  55. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  56. package/dist/types/decorators/attribute.d.ts.map +1 -1
  57. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  58. package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
  59. package/dist/types/decorators/index.d.ts +1 -0
  60. package/dist/types/decorators/index.d.ts.map +1 -1
  61. package/dist/types/decorators/lifecycles.d.ts +2 -0
  62. package/dist/types/decorators/lifecycles.d.ts.map +1 -1
  63. package/dist/types/decorators/setProperty.d.ts +13 -0
  64. package/dist/types/decorators/setProperty.d.ts.map +1 -0
  65. package/dist/types/elements/SwcAppMixin.d.ts +7 -2
  66. package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
  67. package/dist/types/elements/SwcAsync.d.ts.map +1 -1
  68. package/dist/types/elements/SwcChoose.d.ts +1 -1
  69. package/dist/types/elements/SwcChoose.d.ts.map +1 -1
  70. package/dist/types/elements/register.d.ts +3 -1
  71. package/dist/types/elements/register.d.ts.map +1 -1
  72. package/dist/types/index.d.ts +1 -1
  73. package/dist/types/index.d.ts.map +1 -1
  74. package/dist/types/types.d.ts +3 -0
  75. package/dist/types/types.d.ts.map +1 -1
  76. package/dist/types/utils/Utils.d.ts +13 -0
  77. package/dist/types/utils/Utils.d.ts.map +1 -1
  78. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +118 -4
  79. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  80. package/package.json +4 -4
  81. package/src/decorators/attribute.ts +6 -1
  82. package/src/decorators/elementDefine.ts +12 -4
  83. package/src/decorators/emitCustomEvent.ts +1 -0
  84. package/src/decorators/index.ts +1 -0
  85. package/src/decorators/lifecycles.ts +3 -0
  86. package/src/decorators/setProperty.ts +97 -0
  87. package/src/elements/SwcAppEngine.ts +3 -3
  88. package/src/elements/SwcAppMixin.ts +77 -14
  89. package/src/elements/SwcAsync.ts +5 -1
  90. package/src/elements/SwcChoose.ts +76 -37
  91. package/src/elements/register.ts +158 -39
  92. package/src/index.ts +3 -1
  93. package/src/types.ts +3 -0
  94. package/src/utils/Utils.ts +55 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dooboostore/simple-web-component",
3
- "version": "1.0.28",
3
+ "version": "1.0.30",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -92,10 +92,10 @@
92
92
  "webpack": "^5.93.0",
93
93
  "webpack-cli": "^5.1.4",
94
94
  "webpack-dev-server": "^5.2.0",
95
- "@dooboostore/core-node": "1.0.12",
96
- "@dooboostore/core-web": "1.0.10",
97
95
  "@dooboostore/core": "1.0.30",
98
- "@dooboostore/simple-boot": "1.0.54"
96
+ "@dooboostore/simple-boot": "1.0.54",
97
+ "@dooboostore/core-web": "1.0.10",
98
+ "@dooboostore/core-node": "1.0.12"
99
99
  },
100
100
  "scripts": {
101
101
  "clean": "rm -rf dist",
@@ -92,6 +92,7 @@ export function attribute(selector: string, options: AttributeFieldOptions = {})
92
92
 
93
93
  const applySetAttribute = (inst: any, targets: HTMLElement[], attrName: string, nv: any) => {
94
94
  targets.forEach(it => {
95
+ // console.log('sssssssssssss', it, attrName, nv);
95
96
  if (it && typeof (it as any).setAttribute === 'function') {
96
97
  if (nv === null) {
97
98
  (it as any).removeAttribute(attrName);
@@ -112,6 +113,7 @@ export function attribute(selector: string, options: AttributeFieldOptions = {})
112
113
 
113
114
  Object.defineProperty(target, propertyKey, {
114
115
  get(this: any) {
116
+ // console.log('------>??gettttt', this, propertyKey);
115
117
  // try {
116
118
  const attrName = options.name || String(propertyKey);
117
119
  const conf = getElementConfig(this);
@@ -136,7 +138,9 @@ export function attribute(selector: string, options: AttributeFieldOptions = {})
136
138
  return null;
137
139
  }
138
140
 
139
- return convertValue(this[privateKey], targetType);
141
+ const data = convertValue(this[privateKey], targetType);
142
+ // console.log('get rrr', data);
143
+ return data;
140
144
  // } catch (err) {
141
145
  // console.error('Error in getter:', err);
142
146
  // throw err;
@@ -144,6 +148,7 @@ export function attribute(selector: string, options: AttributeFieldOptions = {})
144
148
  },
145
149
  set(this: any, nv: any) {
146
150
  // this[propertyKey] = nv;
151
+ // console.log('------>??settttt', this, nv, propertyKey);
147
152
  const attrName = options.name || String(propertyKey);
148
153
  const conf = getElementConfig(this);
149
154
  const currentWin = this._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as any);
@@ -91,6 +91,7 @@ export const ensureInit = (inst: any) => {
91
91
 
92
92
  // Call constructor script if present
93
93
  const hostSet = SwcUtils.getHostSet(inst);
94
+ const appHosts = SwcUtils.findAllParentAppHostsDirect(inst);
94
95
  inst._executeSwcScript?.('swc-on-constructor', hostSet);
95
96
 
96
97
  // Call @onInitialize lifecycle methods
@@ -172,14 +173,15 @@ const setupPrototype = (proto: any) => {
172
173
  return (typeof window !== 'undefined' ? window : undefined) as Window;
173
174
  };
174
175
 
175
- proto._invokeLifecycleMethod = function (methodName: string | symbol, hostSet: HostSet, extraArgs: any[] = []) {
176
+ proto._invokeLifecycleMethod = function (methodName: string | symbol, hostSet?: HostSet, extraArgs: any[] = []) {
176
177
  ensureInit(this);
177
178
  if (typeof (this as any)[methodName] !== 'function') return;
178
- const app = hostSet.$appHost?.simpleApplication;
179
-
179
+ const useHostSet = hostSet ?? SwcUtils.getHostSet(this);
180
+ const app = useHostSet?.$appHost.simpleApplication;
181
+ // console.log('---->hh',app, this, methodName);
180
182
  if (app) {
181
183
  const otherStorage = new Map<any, any>();
182
- const situations = new SituationTypeContainers([new SituationTypeContainer({ situationType: InjectSituationType.HOST_SET, data: hostSet }), new SituationTypeContainer({ situationType: InjectSituationType.APP_HOST, data: hostSet.$appHost }), new SituationTypeContainer({ situationType: InjectSituationType.APP_HOSTS, data: hostSet.$appHosts }), new SituationTypeContainer({ situationType: InjectSituationType.HOST, data: hostSet.$host }), new SituationTypeContainer({ situationType: InjectSituationType.HOSTS, data: hostSet.$hosts }), new SituationTypeContainer({ situationType: InjectSituationType.FIRST_HOST, data: hostSet.$firstHost }), new SituationTypeContainer({ situationType: InjectSituationType.LAST_HOST, data: hostSet.$lastHost }), new SituationTypeContainer({ situationType: InjectSituationType.FIRST_APP_HOST, data: hostSet.$firstAppHost }), new SituationTypeContainer({ situationType: InjectSituationType.LAST_APP_HOST, data: hostSet.$lastAppHost })]);
184
+ const situations = new SituationTypeContainers([new SituationTypeContainer({ situationType: InjectSituationType.HOST_SET, data: useHostSet }), new SituationTypeContainer({ situationType: InjectSituationType.APP_HOST, data: useHostSet.$appHost }), new SituationTypeContainer({ situationType: InjectSituationType.APP_HOSTS, data: useHostSet.$appHosts }), new SituationTypeContainer({ situationType: InjectSituationType.HOST, data: useHostSet.$host }), new SituationTypeContainer({ situationType: InjectSituationType.HOSTS, data: useHostSet.$hosts }), new SituationTypeContainer({ situationType: InjectSituationType.FIRST_HOST, data: useHostSet.$firstHost }), new SituationTypeContainer({ situationType: InjectSituationType.LAST_HOST, data: useHostSet.$lastHost }), new SituationTypeContainer({ situationType: InjectSituationType.FIRST_APP_HOST, data: useHostSet.$firstAppHost }), new SituationTypeContainer({ situationType: InjectSituationType.LAST_APP_HOST, data: useHostSet.$lastAppHost })]);
183
185
  otherStorage.set(SituationTypeContainers, situations);
184
186
 
185
187
  return app.simstanceManager.executeBindParameterSimPromise(
@@ -493,8 +495,14 @@ export const elementDefine =
493
495
  (this as any).__swc_connected = true;
494
496
 
495
497
  // Register to appHost LAST when all lifecycle completed
498
+ // console.log('!!!!!!!!!!!!!!', this, appHost)
499
+ const appHostss = SwcUtils.findAllParentAppHostsDirect(this)
500
+ // const nAppHostSet = SwcUtils.getHostSet(this as any);
496
501
  if (appHost && typeof (appHost as any)._connected === 'function') {
497
502
  (appHost as any)._connected(this);
503
+ } else if (appHost && typeof (appHost as any)) {
504
+ (appHost as any)._connected_safari ??= [];
505
+ (appHost as any)._connected_safari.push(this);
498
506
  }
499
507
  };
500
508
 
@@ -52,6 +52,7 @@ export function emitCustomEvent(selectorOrTarget: string, type: string, options:
52
52
  const hostSet = SwcUtils.getHostSet(this as any);
53
53
  const conf = getElementConfig(this);
54
54
  const currentWin = (this as any)._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as Window);
55
+ // console.log('emitEEEEEEEEEEE', hostSet);
55
56
 
56
57
  const eventTargets: EventTarget[] = [];
57
58
  if (selectorOrTarget === ':window') eventTargets.push(currentWin);
@@ -10,4 +10,5 @@ export * from './queryAll';
10
10
  export * from './applyClass';
11
11
  export * from './applyStyle';
12
12
  export * from './applyAttribute';
13
+ export * from './setProperty';
13
14
  export * from './subscribeSwcAppRouteChange';
@@ -8,6 +8,7 @@ export const ON_AFTER_DISCONNECTED_METADATA_KEY = Symbol.for('simple-web-compone
8
8
  export const ON_BEFORE_ADOPTED_METADATA_KEY = Symbol.for('simple-web-component:on-before-adopted');
9
9
  export const ON_AFTER_ADOPTED_METADATA_KEY = Symbol.for('simple-web-component:on-after-adopted');
10
10
  export const ON_ADD_EVENT_LISTENER_METADATA_KEY = Symbol.for('simple-web-component:on-add-event-listener');
11
+ export const ON_CONNECTED_SWC_APP_METADATA_KEY = Symbol.for('simple-web-component:on-connected-swc-app');
11
12
  export const INNER_HTML_METADATA_KEY = Symbol.for('simple-web-component:inner-html');
12
13
 
13
14
  export const ATTRIBUTE_CHANGED_WILDCARD = '*';
@@ -48,6 +49,8 @@ export const onAdopted = onAfterAdopted;
48
49
 
49
50
  export const onAddEventListener = createLifecycleDecorator(ON_ADD_EVENT_LISTENER_METADATA_KEY);
50
51
 
52
+ export const onConnectedSwcApp = createLifecycleDecorator(ON_CONNECTED_SWC_APP_METADATA_KEY);
53
+
51
54
  /**
52
55
  * @onConnectedInnerHtml decorator to define the initial HTML content when connected.
53
56
  */
@@ -0,0 +1,97 @@
1
+ import { ReflectUtils } from '@dooboostore/core';
2
+ import { ensureInit } from './elementDefine';
3
+ import { SwcUtils } from '../utils/Utils';
4
+ import { SwcQueryOptions } from '../types';
5
+
6
+ export interface PropertySetOptions extends SwcQueryOptions {
7
+ name?: string;
8
+ }
9
+
10
+ export interface PropertySetMetadata {
11
+ propertyKey: string | symbol;
12
+ selector: string;
13
+ options: PropertySetOptions;
14
+ }
15
+
16
+ export const PROPERTY_SET_METADATA_KEY = Symbol.for('simple-web-component:property-set');
17
+
18
+ export const setProperty = (selector: string, propertyNameOrOptions?: string | PropertySetOptions, options: PropertySetOptions = {}): MethodDecorator => {
19
+ return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
20
+ const constructor = target.constructor;
21
+ let metaList = ReflectUtils.getOwnMetadata(PROPERTY_SET_METADATA_KEY, constructor) as Map<string | symbol, PropertySetMetadata>;
22
+ if (!metaList) {
23
+ metaList = new Map<string | symbol, PropertySetMetadata>();
24
+ ReflectUtils.defineMetadata(PROPERTY_SET_METADATA_KEY, metaList, constructor);
25
+ }
26
+
27
+ let propertyName = typeof propertyNameOrOptions === 'string' ? propertyNameOrOptions : undefined;
28
+ let finalOptions = options;
29
+
30
+ if (typeof propertyNameOrOptions === 'object' && propertyNameOrOptions !== null) {
31
+ finalOptions = propertyNameOrOptions as PropertySetOptions;
32
+ }
33
+
34
+ metaList.set(propertyKey, {
35
+ propertyKey,
36
+ selector,
37
+ options: { ...finalOptions, name: propertyName }
38
+ });
39
+
40
+ const original = descriptor.value;
41
+ descriptor.value = async function (...args: any[]) {
42
+ 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[] = [];
50
+
51
+ const applyRoot = (t: any) => {
52
+ if (!t || !(t instanceof win.HTMLElement)) return;
53
+ targetEls.push(t);
54
+ };
55
+
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[]));
70
+ }
71
+ }
72
+
73
+ targetEls.forEach(targetEl => {
74
+ const resolvedRes = typeof res === 'function' ? (res as any)(targetEl, hostSet) : res;
75
+
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
+ });
90
+ }
91
+ };
92
+ };
93
+ };
94
+
95
+ export const setPropertyHost = (propertyNameOrOptions?: string | PropertySetOptions, options: PropertySetOptions = {}): MethodDecorator => {
96
+ return setProperty(':host', propertyNameOrOptions, options);
97
+ };
@@ -42,11 +42,11 @@ export class SwcAppEngine {
42
42
 
43
43
  // Router Selection
44
44
  if (this._config.routeType === 'path') {
45
- this.router = new PathRouter({ window: this._config.window, firstUrl: this._config.path });
45
+ this.router = new PathRouter({ window: this._config.window });
46
46
  } else if (this._config.routeType === 'hash') {
47
- this.router = new HashRouter({ window: this._config.window, firstUrl: this._config.path });
47
+ this.router = new HashRouter({ window: this._config.window });
48
48
  } else if (this._config.routeType === 'element') {
49
- this.router = new ElementRouter({ window: this._config.window, firstUrl: this._config.path });
49
+ this.router = new ElementRouter({ window: this._config.window });
50
50
  }
51
51
 
52
52
  // console.log('vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv', this.router)
@@ -2,16 +2,25 @@ import { SwcAppEngine, SwcAttributeConfigType, SwcConfigType } from './SwcAppEng
2
2
  import { SwcAppInterface } from '../types';
3
3
  import { SwcUtils } from '../utils/Utils';
4
4
  import { FunctionUtils, Subscription } from '@dooboostore/core';
5
- import { RouterEventType } from '@dooboostore/core-web';
5
+ import { Router, RouterEventType } from '@dooboostore/core-web';
6
+ import { ON_CONNECTED_SWC_APP_METADATA_KEY, findAllLifecycleMetadata } from '../decorators/lifecycles';
6
7
  import { getSubscribeSwcAppRouteChangeMetadata } from '../decorators/subscribeSwcAppRouteChange';
7
8
 
8
9
  export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Base: T) {
10
+ //@ts-ignore
9
11
  return class extends Base implements SwcAppInterface {
10
12
  __swc_engine = new SwcAppEngine(this as any);
11
13
  _swc_connected_instance = new Set<any>();
12
14
  _routerSubscription?: Subscription;
13
15
  _lastRouterEvent?: RouterEventType;
14
16
 
17
+ /*
18
+ 사파리 is 일경우
19
+ <script src="https://unpkg.com/@ungap/custom-elements"></script>
20
+ 이걸사용하는데 is 대응하는거다 그런데 이때 처리되는 순서가 크롬이나 표준에서는 부모부터 만들어지고 자식들이 만들어지는데
21
+ 저 polyfill에서는 자식부터 만들어지고 부모가 만들어진다 is일때 아 .. 이문제때문에 자식이 _connected_safari 에 강제로 자기자식넣어준다
22
+ */
23
+ _connected_safari = [];
15
24
  get simpleApplication() {
16
25
  return this.__swc_engine.simpleApplication;
17
26
  }
@@ -19,6 +28,11 @@ export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Bas
19
28
  return this.__swc_engine.router;
20
29
  }
21
30
 
31
+ // constructor() {
32
+ // super();
33
+ // console.log('cccccccccccccccccccccc');
34
+ // }
35
+
22
36
  _invokeRouteChangeSubscribers(instance: any, re: RouterEventType) {
23
37
  const routeChangeSubscribers = getSubscribeSwcAppRouteChangeMetadata(instance);
24
38
  if (routeChangeSubscribers && Array.isArray(routeChangeSubscribers)) {
@@ -56,20 +70,22 @@ export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Bas
56
70
 
57
71
  // Invoke method if conditions pass
58
72
  if (pathMatched && filterPassed) {
59
- instance[methodName]?.(re, pathData || {});
73
+ instance[methodName]?.({...re, pathData: pathData});
60
74
  }
61
75
  });
62
76
  }
63
77
  }
64
78
 
65
79
  _connected(instance: any) {
66
- // console.log('------------------>', instance);
80
+ // console.log('appmixin--> connected', this, instance);
67
81
  if (instance) {
68
82
  this._swc_connected_instance.add(instance);
69
-
70
- // Notify current route subscribers immediately after connection
71
- if (this.router && this._lastRouterEvent) {
72
- this._invokeRouteChangeSubscribers(instance, this._lastRouterEvent);
83
+ if (this.simpleApplication) {
84
+ this._invokeOnConnectedSwcAppStarted(instance);
85
+ // Notify current route subscribers immediately after connection
86
+ if (this.router && this._lastRouterEvent) {
87
+ this._invokeRouteChangeSubscribers(instance, this._lastRouterEvent);
88
+ }
73
89
  }
74
90
  }
75
91
  }
@@ -127,19 +143,54 @@ export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Bas
127
143
  }
128
144
  }
129
145
 
130
- // console.log('vvvvvvv222222', this.router);
146
+ // 사파리일떄 예외이다 is 일때 polyfill되는데 이떄 부모부터 만들어지는게 아니라 자식부터 만들어진다 그래서 부모가 만들어질때 자식들이 이미 만들어져있어서 부모가 자식들을 등록해주는 형태로 강제로 등록해준다
147
+ this._connected_safari.forEach((instance: any) => {
148
+ this._swc_connected_instance.add(instance);
149
+ });
150
+ // Invoke @onConnectedSwcApp lifecycle methods on all connected component instances
151
+ // const hostSet = SwcUtils.getHostSet(this as any);
152
+ this._swc_connected_instance.forEach((instance: any) => {
153
+ this._invokeOnConnectedSwcAppStarted(instance);
154
+ });
155
+
156
+ // Safari fix: Manually scan DOM tree and register any SWC components that couldn't find parent
157
+ // This handles the case where is="" elements get upgraded in child-first order
158
+ // const allElements = (this as any).querySelectorAll('*');
159
+ // for (const el of allElements) {
160
+ // const isConnected = (el as any).__swc_connected;
161
+ // if (!isConnected && typeof (el as any)._resolveWindow === 'function') {
162
+ // // Element is an SWC component but hasn't registered with app yet
163
+ // try {
164
+ // this._connected(el);
165
+ // if ((el as any)._invokeOnConnectedSwcAppStarted) {
166
+ // this._invokeOnConnectedSwcAppStarted(el);
167
+ // }
168
+ // } catch (e) {
169
+ // console.warn('[SWC] Error registering discovered element:', el.tagName, e);
170
+ // }
171
+ // }
172
+ // }
173
+
131
174
  // Subscribe to router changes after connect
132
175
  if (!this._routerSubscription && this.router) {
133
176
  this._routerSubscription = this.router.observable.subscribe((route: RouterEventType) => {
134
177
  // console.log('Route changed:', route);
135
178
  this._handleRouteChange(route);
136
179
  });
137
-
138
180
  // Navigate to initial path if specified
139
- // const config = this.__swc_engine.config;
140
- // if (config?.path) {
141
- // this.router.go(config.path);
142
- // }
181
+ const config = this.__swc_engine.config;
182
+ if (config?.path) {
183
+ this.router.go(config.path);
184
+ }
185
+ }
186
+ }
187
+
188
+ _invokeOnConnectedSwcAppStarted(instance: any) {
189
+ const startedMethods = findAllLifecycleMetadata(instance, ON_CONNECTED_SWC_APP_METADATA_KEY);
190
+ if (startedMethods) {
191
+ for (const m of startedMethods) {
192
+ instance._invokeLifecycleMethod(m);
193
+ }
143
194
  }
144
195
  }
145
196
 
@@ -177,7 +228,19 @@ export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Bas
177
228
  }
178
229
 
179
230
  async routing(path: string) {
180
- await this.__swc_engine.router?.go(path);
231
+ await this.router?.go(path);
232
+ }
233
+
234
+ back(): void {
235
+ this.router?.back();
236
+ }
237
+
238
+ forward(): void {
239
+ this.router?.forward();
240
+ }
241
+
242
+ reload(): void {
243
+ this.router?.reload();
181
244
  }
182
245
  };
183
246
  }
@@ -31,7 +31,10 @@ export class SwcAsync extends HTMLTemplateElement {
31
31
  @changedAttributeHost('on-get-value')
32
32
  private refresh() {
33
33
  const script = this.getAttribute('on-get-value');
34
- if (!script) return;
34
+ if (!script) {
35
+ this.render('default');
36
+ return;
37
+ }
35
38
  const win = this.ownerDocument?.defaultView || window;
36
39
  const result = FunctionUtils.executeReturn({ script, context: this, args: SwcUtils.getHelperAndHostSet(win, this) });
37
40
 
@@ -85,6 +88,7 @@ export class SwcAsync extends HTMLTemplateElement {
85
88
  }
86
89
 
87
90
  connectedCallback() {
91
+ console.log('[SwcAsync] connectedCallback called');
88
92
  this.refresh();
89
93
  }
90
94
  disconnectedCallback() {
@@ -5,6 +5,7 @@ import { FunctionUtils } from '@dooboostore/core';
5
5
  export class SwcChoose extends HTMLTemplateElement {
6
6
  private _nodes: Node[] = [];
7
7
  private _value: any = null;
8
+ private _previousTemplate: HTMLTemplateElement | null = null;
8
9
 
9
10
  get value(): any {
10
11
  return this._value;
@@ -14,76 +15,114 @@ export class SwcChoose extends HTMLTemplateElement {
14
15
  this.render();
15
16
  }
16
17
 
17
- private _resolvePortal(): Element | null {
18
- const portalScript = this.getAttribute('on-get-portal');
19
- if (!portalScript) return this.parentElement;
20
-
21
- const win = this.ownerDocument?.defaultView || window;
22
- const res = FunctionUtils.executeReturn({
23
- script: portalScript,
24
- context: this,
25
- args: SwcUtils.getHelperAndHostSet(win, this)
26
- });
27
-
28
- if (res instanceof HTMLElement) return res;
29
- if (typeof res === 'string') return win.document.querySelector(res);
30
- return this.parentElement;
31
- }
32
-
33
18
  private _executeCloneCallback(attr: string, args: any) {
34
19
  const script = this.getAttribute(attr);
35
20
  if (!script) return;
36
21
  FunctionUtils.execute({ script, context: this, args });
37
22
  }
38
23
 
39
- @changedAttributeHost('on-get-value')
40
- private refresh() {
41
- const script = this.getAttribute('on-get-value');
42
- if (!script) {
24
+ @changedAttributeHost('value')
25
+ private refresh(valueAttr?: string | null) {
26
+ valueAttr ??= this.getAttribute('value');
27
+ if (valueAttr === null) {
43
28
  this.value = null;
44
29
  return;
45
30
  }
46
- const win = this.ownerDocument?.defaultView || window;
47
- const result = FunctionUtils.executeReturn({ script, context: this, args: SwcUtils.getHelperAndHostSet(win, this) });
48
- this.value = result;
31
+
32
+ // {{ }} 브레이스로 감싸진 값은 스크립트 실행
33
+ if (valueAttr.startsWith('{{') && valueAttr.endsWith('}}')) {
34
+ const script = valueAttr.slice(2, -2).trim();
35
+ const win = this.ownerDocument?.defaultView || window;
36
+ const result = FunctionUtils.executeReturn({ script, context: this, args: SwcUtils.getHelperAndHostSet(win, this) });
37
+ this.value = result;
38
+ } else {
39
+ // 일반 문자열 값
40
+ this.value = valueAttr;
41
+ }
49
42
  }
50
43
 
51
44
  private render() {
52
- this.cleanup();
53
- const portal = this._resolvePortal();
54
- if (!portal) return;
55
-
56
45
  const win = this.ownerDocument?.defaultView || window;
57
46
  const helperSet = SwcUtils.getHelperAndHostSet(win, this);
58
-
59
47
  const templates = Array.from(this.content.querySelectorAll('template'));
60
48
  let selected: HTMLTemplateElement | null = null;
49
+
61
50
  for (const t of templates) {
62
- if (t.getAttribute('is') === 'swc-when' && String(t.getAttribute('value')) === String(this._value)) {
63
- selected = t;
64
- break;
51
+ if (t.getAttribute('is') === 'swc-when') {
52
+ let matches = false;
53
+ const value = t.getAttribute('value') || '';
54
+
55
+ // {{ }} 브레이스로 감싸진 값은 스크립트 실행
56
+ if (value.startsWith('{{') && value.endsWith('}}')) {
57
+ const script = value.slice(2, -2).trim();
58
+ const result = FunctionUtils.executeReturn({
59
+ script,
60
+ context: t,
61
+ args: { ...helperSet, $value: this._value }
62
+ });
63
+ // console.log('rrrrrrrrrrrrrr', result);
64
+ matches = result === true;
65
+ } else {
66
+ // 일반 문자열 비교 (기존 방식)
67
+ matches = String(value) === String(this._value);
68
+ }
69
+
70
+ if (matches) {
71
+ selected = t;
72
+ break;
73
+ }
65
74
  }
66
75
  }
76
+
67
77
  const target = selected || (this.content.querySelector('template[is="swc-otherwise"]') as HTMLTemplateElement);
68
78
 
79
+ // skip-if-same attribute가 있고 이전과 같은 template이면 렌더링 스킵
80
+ // 최상위 skip-if-same 또는 각 when/otherwise의 skip-if-same 체크
81
+ const targetHasSkipIfSame = target?.hasAttribute('skip-if-same');
82
+ const shouldSkip = (this.hasAttribute('skip-if-same') || targetHasSkipIfSame) && this._previousTemplate === target;
83
+ if (shouldSkip) {
84
+ return;
85
+ }
86
+
87
+ this.cleanup();
88
+ this._previousTemplate = target;
89
+
69
90
  if (target) {
70
91
  const clone = target.content.cloneNode(true) as DocumentFragment;
71
92
  this._nodes = Array.from(clone.childNodes);
72
93
  const elements = this._nodes.filter(n => n.nodeType === 1) as HTMLElement[];
73
94
  const groupArgs = { ...helperSet, $value: this._value, $nodes: this._nodes, $elements: elements, $firstElement: elements[0] };
74
95
 
96
+ // 복제된 최상위 요소들은 SwcChoose의 직접 자식이 될 것이므로 __swc_host 설정 안 함
75
97
  this._nodes.forEach((n, nodeIdx) => {
76
- if (n instanceof HTMLElement) (n as any).__swc_host = this;
98
+ // if (n instanceof HTMLElement) (n as any).__swc_host = this; // 제거: 부모 체인 유지
77
99
  this._executeCloneCallback('on-clone-node', { ...groupArgs, $node: n, $nodeIndex: nodeIdx, $isElement: n.nodeType === 1 });
100
+
101
+ // Attribute {{ }} 치환 - HTML string 차원에서 동적 치환
102
+ if (n instanceof HTMLElement) {
103
+ Array.from(n.attributes).forEach(attr => {
104
+ const value = attr.value || '';
105
+ if (value.startsWith('{{') && value.endsWith('}}')) {
106
+ const script = value.slice(2, -2).trim();
107
+ const result = FunctionUtils.executeReturn({
108
+ script,
109
+ context: n,
110
+ args: { ...groupArgs, $value: this._value }
111
+ });
112
+ n.setAttribute(attr.name, String(result));
113
+ }
114
+ });
115
+ }
78
116
  });
79
117
 
80
118
  this._executeCloneCallback('on-clone-nodes', groupArgs);
81
119
 
82
- if (portal === this.parentElement) {
83
- portal.insertBefore(clone, this.nextSibling);
84
- } else {
85
- portal.appendChild(clone);
86
- }
120
+ this.parentElement?.insertBefore(clone, this.nextSibling);
121
+ // if (portal === this.parentElement) {
122
+ // portal.insertBefore(clone, this.nextSibling);
123
+ // } else {
124
+ // portal.appendChild(clone);
125
+ // }
87
126
  }
88
127
  }
89
128