@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
@@ -4,7 +4,9 @@ import { SwcUtils } from '../utils/Utils';
4
4
  import { FunctionUtils } from '@dooboostore/core';
5
5
  import { changedAttributeHost } from '../decorators/changedAttributeHost';
6
6
 
7
- export const registerAllElements = (w: any) => {
7
+ type Constructor<T> = new (...args: any[]) => T;
8
+
9
+ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElement>> => {
8
10
  const {
9
11
  HTMLElement: _HTMLElement,
10
12
  HTMLTemplateElement: _HTMLTemplateElement,
@@ -374,6 +376,7 @@ export const registerAllElements = (w: any) => {
374
376
  class SwcChoose extends HTMLTemplateElement {
375
377
  private _nodes: Node[] = [];
376
378
  private _value: any = null;
379
+ private _previousTemplate: HTMLTemplateElement | null = null;
377
380
 
378
381
  get value(): any {
379
382
  return this._value;
@@ -383,76 +386,114 @@ export const registerAllElements = (w: any) => {
383
386
  this.render();
384
387
  }
385
388
 
386
- private _resolvePortal(): Element | null {
387
- const portalScript = this.getAttribute('on-get-portal');
388
- if (!portalScript) return this.parentElement;
389
-
390
- const win = this.ownerDocument?.defaultView || window;
391
- const res = FunctionUtils.executeReturn({
392
- script: portalScript,
393
- context: this,
394
- args: SwcUtils.getHelperAndHostSet(win, this)
395
- });
396
-
397
- if (res instanceof HTMLElement) return res;
398
- if (typeof res === 'string') return win.document.querySelector(res);
399
- return this.parentElement;
400
- }
401
-
402
389
  private _executeCloneCallback(attr: string, args: any) {
403
390
  const script = this.getAttribute(attr);
404
391
  if (!script) return;
405
392
  FunctionUtils.execute({ script, context: this, args });
406
393
  }
407
394
 
408
- @changedAttributeHost('on-get-value')
409
- private refresh() {
410
- const script = this.getAttribute('on-get-value');
411
- if (!script) {
395
+ @changedAttributeHost('value')
396
+ private refresh(valueAttr?: string | null) {
397
+ valueAttr ??= this.getAttribute('value');
398
+ if (valueAttr === null) {
412
399
  this.value = null;
413
400
  return;
414
401
  }
415
- const win = this.ownerDocument?.defaultView || window;
416
- const result = FunctionUtils.executeReturn({ script, context: this, args: SwcUtils.getHelperAndHostSet(win, this) });
417
- this.value = result;
402
+
403
+ // {{ }} 브레이스로 감싸진 값은 스크립트 실행
404
+ if (valueAttr.startsWith('{{') && valueAttr.endsWith('}}')) {
405
+ const script = valueAttr.slice(2, -2).trim();
406
+ const win = this.ownerDocument?.defaultView || window;
407
+ const result = FunctionUtils.executeReturn({ script, context: this, args: SwcUtils.getHelperAndHostSet(win, this) });
408
+ this.value = result;
409
+ } else {
410
+ // 일반 문자열 값
411
+ this.value = valueAttr;
412
+ }
418
413
  }
419
414
 
420
415
  private render() {
421
- this.cleanup();
422
- const portal = this._resolvePortal();
423
- if (!portal) return;
424
-
425
416
  const win = this.ownerDocument?.defaultView || window;
426
417
  const helperSet = SwcUtils.getHelperAndHostSet(win, this);
427
-
428
418
  const templates = Array.from(this.content.querySelectorAll('template'));
429
419
  let selected: HTMLTemplateElement | null = null;
420
+
430
421
  for (const t of templates) {
431
- if (t.getAttribute('is') === 'swc-when' && String(t.getAttribute('value')) === String(this._value)) {
432
- selected = t;
433
- break;
422
+ if (t.getAttribute('is') === 'swc-when') {
423
+ let matches = false;
424
+ const value = t.getAttribute('value') || '';
425
+
426
+ // {{ }} 브레이스로 감싸진 값은 스크립트 실행
427
+ if (value.startsWith('{{') && value.endsWith('}}')) {
428
+ const script = value.slice(2, -2).trim();
429
+ const result = FunctionUtils.executeReturn({
430
+ script,
431
+ context: t,
432
+ args: { ...helperSet, $value: this._value }
433
+ });
434
+ // console.log('rrrrrrrrrrrrrr', result);
435
+ matches = result === true;
436
+ } else {
437
+ // 일반 문자열 비교 (기존 방식)
438
+ matches = String(value) === String(this._value);
439
+ }
440
+
441
+ if (matches) {
442
+ selected = t;
443
+ break;
444
+ }
434
445
  }
435
446
  }
447
+
436
448
  const target = selected || (this.content.querySelector('template[is="swc-otherwise"]') as HTMLTemplateElement);
437
449
 
450
+ // skip-if-same attribute가 있고 이전과 같은 template이면 렌더링 스킵
451
+ // 최상위 skip-if-same 또는 각 when/otherwise의 skip-if-same 체크
452
+ const targetHasSkipIfSame = target?.hasAttribute('skip-if-same');
453
+ const shouldSkip = (this.hasAttribute('skip-if-same') || targetHasSkipIfSame) && this._previousTemplate === target;
454
+ if (shouldSkip) {
455
+ return;
456
+ }
457
+
458
+ this.cleanup();
459
+ this._previousTemplate = target;
460
+
438
461
  if (target) {
439
462
  const clone = target.content.cloneNode(true) as DocumentFragment;
440
463
  this._nodes = Array.from(clone.childNodes);
441
464
  const elements = this._nodes.filter(n => n.nodeType === 1) as HTMLElement[];
442
465
  const groupArgs = { ...helperSet, $value: this._value, $nodes: this._nodes, $elements: elements, $firstElement: elements[0] };
443
466
 
467
+ // 복제된 최상위 요소들은 SwcChoose의 직접 자식이 될 것이므로 __swc_host 설정 안 함
444
468
  this._nodes.forEach((n, nodeIdx) => {
445
- if (n instanceof HTMLElement) (n as any).__swc_host = this;
469
+ // if (n instanceof HTMLElement) (n as any).__swc_host = this; // 제거: 부모 체인 유지
446
470
  this._executeCloneCallback('on-clone-node', { ...groupArgs, $node: n, $nodeIndex: nodeIdx, $isElement: n.nodeType === 1 });
471
+
472
+ // Attribute {{ }} 치환 - HTML string 차원에서 동적 치환
473
+ if (n instanceof HTMLElement) {
474
+ Array.from(n.attributes).forEach(attr => {
475
+ const value = attr.value || '';
476
+ if (value.startsWith('{{') && value.endsWith('}}')) {
477
+ const script = value.slice(2, -2).trim();
478
+ const result = FunctionUtils.executeReturn({
479
+ script,
480
+ context: n,
481
+ args: { ...groupArgs, $value: this._value }
482
+ });
483
+ n.setAttribute(attr.name, String(result));
484
+ }
485
+ });
486
+ }
447
487
  });
448
488
 
449
489
  this._executeCloneCallback('on-clone-nodes', groupArgs);
450
490
 
451
- if (portal === this.parentElement) {
452
- portal.insertBefore(clone, this.nextSibling);
453
- } else {
454
- portal.appendChild(clone);
455
- }
491
+ this.parentElement?.insertBefore(clone, this.nextSibling);
492
+ // if (portal === this.parentElement) {
493
+ // portal.insertBefore(clone, this.nextSibling);
494
+ // } else {
495
+ // portal.appendChild(clone);
496
+ // }
456
497
  }
457
498
  }
458
499
 
@@ -499,7 +540,10 @@ export const registerAllElements = (w: any) => {
499
540
  @changedAttributeHost('on-get-value')
500
541
  private refresh() {
501
542
  const script = this.getAttribute('on-get-value');
502
- if (!script) return;
543
+ if (!script) {
544
+ this.render('default');
545
+ return;
546
+ }
503
547
  const win = this.ownerDocument?.defaultView || window;
504
548
  const result = FunctionUtils.executeReturn({ script, context: this, args: SwcUtils.getHelperAndHostSet(win, this) });
505
549
 
@@ -553,6 +597,7 @@ export const registerAllElements = (w: any) => {
553
597
  }
554
598
 
555
599
  connectedCallback() {
600
+ console.log('[SwcAsync] connectedCallback called');
556
601
  this.refresh();
557
602
  }
558
603
  disconnectedCallback() {
@@ -732,4 +777,78 @@ export const registerAllElements = (w: any) => {
732
777
  @elementDefine('swc-app-body', { extends: 'body', window: w })
733
778
  class SwcAppBody extends SwcAppMixin(HTMLBodyElement) {}
734
779
 
780
+
781
+ return {
782
+ // Sub Templates
783
+ SwcLoading,
784
+ SwcError,
785
+ SwcSuccess,
786
+ SwcWhen,
787
+ SwcOtherwise,
788
+ SwcDefault,
789
+ // Core Elements
790
+ SwcApp,
791
+ SwcIf,
792
+ SwcLoop,
793
+ SwcChoose,
794
+ SwcAsync,
795
+ // App Elements
796
+ SwcAppAnchor,
797
+ SwcAppArea,
798
+ SwcAppAudio,
799
+ SwcAppBase,
800
+ SwcAppButton,
801
+ SwcAppCanvas,
802
+ SwcAppData,
803
+ SwcAppDataList,
804
+ SwcAppDetails,
805
+ SwcAppDialog,
806
+ SwcAppDiv,
807
+ SwcAppDList,
808
+ SwcAppEmbed,
809
+ SwcAppFieldSet,
810
+ SwcAppForm,
811
+ SwcAppHR,
812
+ SwcAppIFrame,
813
+ SwcAppImage,
814
+ SwcAppInput,
815
+ SwcAppLabel,
816
+ SwcAppLegend,
817
+ SwcAppLI,
818
+ SwcAppLink,
819
+ SwcAppMap,
820
+ SwcAppMeta,
821
+ SwcAppMeter,
822
+ SwcAppMod,
823
+ SwcAppObject,
824
+ SwcAppOList,
825
+ SwcAppOptGroup,
826
+ SwcAppOption,
827
+ SwcAppOutput,
828
+ SwcAppParagraph,
829
+ SwcAppParam,
830
+ SwcAppPicture,
831
+ SwcAppPre,
832
+ SwcAppProgress,
833
+ SwcAppQuote,
834
+ SwcAppScript,
835
+ SwcAppSelect,
836
+ SwcAppSlot,
837
+ SwcAppSource,
838
+ SwcAppSpan,
839
+ SwcAppStyle,
840
+ SwcAppTable,
841
+ SwcAppTableSection,
842
+ SwcAppTableCell,
843
+ SwcAppTemplate,
844
+ SwcAppTextArea,
845
+ SwcAppTime,
846
+ SwcAppTitle,
847
+ SwcAppTableRow,
848
+ SwcAppTrack,
849
+ SwcAppUList,
850
+ SwcAppVideo,
851
+ SwcAppHeading,
852
+ SwcAppBody,
853
+ };
735
854
  };
package/src/index.ts CHANGED
@@ -16,12 +16,14 @@ export * from './config/config';
16
16
 
17
17
  // Registration Factory
18
18
  export default (w: Window, other?: (((w: Window) => ConstructorType<any>) | any) []) => {
19
- registerAllElements(w);
19
+ const cr = registerAllElements(w);
20
20
  const r = other?.map(fn => {
21
21
  if (typeof fn === 'function') {
22
22
  const a = fn(w);
23
23
  } else {
24
24
  }
25
25
  });
26
+
27
+ return cr;
26
28
  // return r??[];
27
29
  };
package/src/types.ts CHANGED
@@ -48,4 +48,7 @@ export interface SwcAppInterface extends HTMLElement {
48
48
  router?: Router;
49
49
  connect(config?: SwcAttributeConfigType): Promise<void>;
50
50
  routing(path: string): Promise<void>;
51
+ reload(): void;
52
+ back(): void;
53
+ forward(): void;
51
54
  }
@@ -236,6 +236,59 @@ export namespace SwcUtils {
236
236
  return appHostsWithoutSelf.length > 0 ? appHostsWithoutSelf[appHostsWithoutSelf.length - 1] : undefined;
237
237
  };
238
238
 
239
+ /**
240
+ * Find parent app host by traversing DOM tree directly.
241
+ * Only checks tagName and is attribute - no SWC host discovery.
242
+ * Useful for Safari polyfill compatibility where is="" elements upgrade in child-first order.
243
+ */
244
+ export const findParentAppHostDirect = (el: HTMLElement): SwcAppInterface | null => {
245
+ const appHosts = findAllParentAppHostsDirect(el);
246
+ return appHosts.length > 0 ? appHosts[appHosts.length - 1] : null;
247
+ };
248
+
249
+ /**
250
+ * Find all parent app hosts by traversing DOM tree directly.
251
+ * Returns array of all parent app hosts in [closest-parent, ..., root] order.
252
+ * Only checks tagName and is attribute - no SWC host discovery.
253
+ * Useful for Safari polyfill compatibility where is="" elements upgrade in child-first order.
254
+ */
255
+ export const findAllParentAppHostsDirect = (el: HTMLElement): SwcAppInterface[] => {
256
+ const appHosts: SwcAppInterface[] = [];
257
+
258
+ // Shadow DOM 내부의 depth를 정상적으로 탐색하기 위해
259
+ // el.parentElement부터 시작하고, shadow root 경계를 만날 때만 host로 이동
260
+ let current: HTMLElement | null = el.parentElement;
261
+
262
+ while (current) {
263
+ // console.log('vvvvvvv');
264
+ // alert(1)
265
+ // debugger;
266
+ const isAppHost =
267
+ current.tagName.toLowerCase() === 'swc-app' ||
268
+ current.getAttribute('is')?.startsWith('swc-app-');
269
+ if (isAppHost) {
270
+ appHosts.push(current as SwcAppInterface);
271
+ }
272
+ // Handle shadow DOM
273
+ if (current.shadowRoot) {
274
+ current = ((current.shadowRoot as any).host as HTMLElement).parentElement;
275
+ } else {
276
+ // 부모가 없으면 shadow root 경계일 수 있음
277
+ if (!current.parentElement) {
278
+ const parentNode = current.parentNode;
279
+ if (parentNode instanceof ShadowRoot) {
280
+ current = (parentNode as any).host as HTMLElement;
281
+ } else {
282
+ break;
283
+ }
284
+ } else {
285
+ current = current.parentElement;
286
+ }
287
+ }
288
+ }
289
+ return appHosts.reverse(); // [root, ..., closest-parent]
290
+ };
291
+
239
292
  export const getHostSet = (el: HTMLElement): HostSet => {
240
293
  const isSwc = !!getElementConfig(el);
241
294
  const ancestors = findAllSwcHosts(el); // [root, ..., parent]
@@ -265,7 +318,8 @@ export namespace SwcUtils {
265
318
  curr = curr.parentElement || (curr.getRootNode() as any).host;
266
319
  }
267
320
 
268
- const $appHosts = $hosts.filter(h => h.tagName.toLowerCase() === 'swc-app' || h.getAttribute('is')?.startsWith('swc-app-')) as SwcAppInterface[];
321
+ const $appHosts = SwcUtils.findAllParentAppHostsDirect(el); //$hosts.filter(h => h.tagName.toLowerCase() === 'swc-app' || h.getAttribute('is')?.startsWith('swc-app-')) as SwcAppInterface[];
322
+ // console.log('apphosts00000000', $appHosts)
269
323
  const $appHost = $appHosts.length > 0 ? ($appHosts[$appHosts.length - 1] === el ? ($appHosts.length > 1 ? $appHosts[$appHosts.length - 2] : null) : $appHosts[$appHosts.length - 1]) : null;
270
324
  const $firstAppHost = $appHosts.length > 0 ? $appHosts[0] : null;
271
325