@sellmate/design-system 1.0.74 → 1.0.76

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 (182) hide show
  1. package/dist/cjs/design-system.cjs.js +1 -1
  2. package/dist/cjs/index.cjs.js +5 -0
  3. package/dist/cjs/loader.cjs.js +1 -1
  4. package/dist/cjs/sd-button_4.cjs.entry.js +2 -2
  5. package/dist/cjs/sd-confirm-modal_2.cjs.entry.js +1 -1
  6. package/dist/cjs/sd-ghost-button.cjs.entry.js +10 -5
  7. package/dist/cjs/sd-modal-container.cjs.entry.js +76 -68
  8. package/dist/cjs/sd-pagination_5.cjs.entry.js +889 -0
  9. package/dist/cjs/sd-radio-button.cjs.entry.js +6 -1
  10. package/dist/cjs/sd-select-v2-list-item_4.cjs.entry.js +65 -5
  11. package/dist/cjs/sd-switch.cjs.entry.js +1 -1
  12. package/dist/cjs/sd-table.cjs.entry.js +167 -20
  13. package/dist/cjs/sd-tabs.cjs.entry.js +1 -1
  14. package/dist/cjs/sd-tag.cjs.entry.js +2 -2
  15. package/dist/cjs/sd-td.cjs.entry.js +53 -1
  16. package/dist/cjs/sd-text-link.cjs.entry.js +3 -3
  17. package/dist/cjs/sd-textarea.cjs.entry.js +1 -1
  18. package/dist/cjs/sd-toast-container.cjs.entry.js +1 -1
  19. package/dist/cjs/sd-toast.cjs.entry.js +2 -2
  20. package/dist/cjs/sd-toggle.cjs.entry.js +1 -1
  21. package/dist/collection/components/sd-ghost-button/sd-ghost-button.js +10 -5
  22. package/dist/collection/components/sd-loading-modal/sd-loading-modal.js +3 -3
  23. package/dist/collection/components/sd-modal-container/sd-modal-container.js +77 -71
  24. package/dist/collection/components/sd-radio-button/sd-radio-button.js +6 -1
  25. package/dist/collection/components/sd-select-v2/sd-select-v2-listbox/sd-select-v2-listbox.js +103 -3
  26. package/dist/collection/components/sd-select-v2/sd-select-v2-trigger/sd-select-v2-trigger.js +2 -2
  27. package/dist/collection/components/sd-select-v2/sd-select-v2.js +82 -4
  28. package/dist/collection/components/sd-switch/sd-switch.js +1 -1
  29. package/dist/collection/components/sd-table/sd-table.css +1 -1
  30. package/dist/collection/components/sd-table/sd-table.js +170 -21
  31. package/dist/collection/components/sd-table/sd-tbody/sd-tbody.js +7 -2
  32. package/dist/collection/components/sd-table/sd-td/sd-td.js +91 -1
  33. package/dist/collection/components/sd-table/sd-thead/sd-thead.js +9 -4
  34. package/dist/collection/components/sd-table/sd-tr/sd-tr.css +8 -0
  35. package/dist/collection/components/sd-table/sd-tr/sd-tr.js +62 -12
  36. package/dist/collection/components/sd-tabs/sd-tabs.js +1 -1
  37. package/dist/collection/components/sd-tag/sd-tag.js +2 -2
  38. package/dist/collection/components/sd-text-link/sd-text-link.js +3 -3
  39. package/dist/collection/components/sd-textarea/sd-textarea.js +1 -1
  40. package/dist/collection/components/sd-toast/sd-toast.js +2 -2
  41. package/dist/collection/components/sd-toast-container/sd-toast-container.js +1 -1
  42. package/dist/collection/components/sd-toggle/sd-toggle.js +1 -1
  43. package/dist/collection/components/sd-tooltip/sd-tooltip.js +2 -2
  44. package/dist/collection/utils/modal.js +5 -0
  45. package/dist/components/index.js +1 -1
  46. package/dist/components/{p-BALOEavB.js → p-6AvsuYqF.js} +1 -1
  47. package/dist/components/{p-CTwEbxRN.js → p-6PsyRF61.js} +1 -1
  48. package/dist/components/{p-DEBakAhm.js → p-7DKZPPev.js} +1 -1
  49. package/dist/components/p-BBD_1E3n.js +1 -0
  50. package/dist/components/p-BQvugXhH.js +1 -0
  51. package/dist/components/p-BRfPoWUn.js +1 -0
  52. package/dist/components/{p-CHFGWh0m.js → p-C-BOe23n.js} +1 -1
  53. package/dist/components/p-C7h8lwnU.js +1 -0
  54. package/dist/components/{p-SDBnyM8D.js → p-CUg9NH6y.js} +1 -1
  55. package/dist/components/{p-C3dI7f7C.js → p-CgMyz4NQ.js} +1 -1
  56. package/dist/components/p-Csfj4h1A.js +1 -0
  57. package/dist/components/{p-Bp0B8tcl.js → p-DAC3TaZV.js} +1 -1
  58. package/dist/components/p-DQfNwvwx.js +1 -0
  59. package/dist/components/p-DfOYYI9m.js +1 -0
  60. package/dist/components/{p-H-9uoufd.js → p-d4UB2UF7.js} +1 -1
  61. package/dist/components/p-eEC3ITv0.js +1 -0
  62. package/dist/components/{p-CWEeXx2E.js → p-nVHDJc9g.js} +1 -1
  63. package/dist/components/{p-D8fG9Yt7.js → p-rnbt1m4L.js} +1 -1
  64. package/dist/components/sd-action-modal.js +1 -1
  65. package/dist/components/sd-barcode-input.js +1 -1
  66. package/dist/components/sd-chip.js +1 -1
  67. package/dist/components/sd-confirm-modal.js +1 -1
  68. package/dist/components/sd-date-picker-calendar.js +1 -1
  69. package/dist/components/sd-date-picker.js +1 -1
  70. package/dist/components/sd-date-range-picker-calendar.js +1 -1
  71. package/dist/components/sd-date-range-picker.js +1 -1
  72. package/dist/components/sd-field.js +1 -1
  73. package/dist/components/sd-file-picker.js +1 -1
  74. package/dist/components/sd-ghost-button.js +1 -1
  75. package/dist/components/sd-guide.js +1 -1
  76. package/dist/components/sd-input.js +1 -1
  77. package/dist/components/sd-loading-modal.js +1 -1
  78. package/dist/components/sd-modal-container.js +1 -1
  79. package/dist/components/sd-number-input.js +1 -1
  80. package/dist/components/sd-popover.js +1 -1
  81. package/dist/components/sd-radio-button.js +1 -1
  82. package/dist/components/sd-select-dropdown.js +1 -1
  83. package/dist/components/sd-select-group.js +1 -1
  84. package/dist/components/sd-select-multiple-group.js +1 -1
  85. package/dist/components/sd-select-multiple.js +1 -1
  86. package/dist/components/sd-select-search-input.js +1 -1
  87. package/dist/components/sd-select-v2-listbox.js +1 -1
  88. package/dist/components/sd-select-v2-trigger.js +1 -1
  89. package/dist/components/sd-select-v2.js +1 -1
  90. package/dist/components/sd-select.js +1 -1
  91. package/dist/components/sd-switch.js +1 -1
  92. package/dist/components/sd-table.js +1 -1
  93. package/dist/components/sd-tabs.js +1 -1
  94. package/dist/components/sd-tag.js +1 -1
  95. package/dist/components/sd-tbody.js +1 -1
  96. package/dist/components/sd-td.js +1 -1
  97. package/dist/components/sd-text-link.js +1 -1
  98. package/dist/components/sd-textarea.js +1 -1
  99. package/dist/components/sd-thead.js +1 -1
  100. package/dist/components/sd-toast-container.js +1 -1
  101. package/dist/components/sd-toast.js +1 -1
  102. package/dist/components/sd-toggle.js +1 -1
  103. package/dist/components/sd-tooltip.js +1 -1
  104. package/dist/components/sd-tr.js +1 -1
  105. package/dist/design-system/design-system.esm.js +1 -1
  106. package/dist/design-system/index.esm.js +1 -1
  107. package/dist/design-system/p-0e1b27cc.entry.js +1 -0
  108. package/dist/design-system/p-11029f6e.entry.js +1 -0
  109. package/dist/design-system/{p-cc62c180.entry.js → p-140b40ab.entry.js} +1 -1
  110. package/dist/design-system/p-34f7345b.entry.js +1 -0
  111. package/dist/design-system/p-363c9451.entry.js +1 -0
  112. package/dist/design-system/{p-fdcfaa7c.entry.js → p-506f2b68.entry.js} +1 -1
  113. package/dist/design-system/{p-8200b5f2.entry.js → p-531a6a82.entry.js} +1 -1
  114. package/dist/design-system/p-55b65a41.entry.js +1 -0
  115. package/dist/design-system/{p-d1dfa0e1.entry.js → p-68d0d67e.entry.js} +1 -1
  116. package/dist/design-system/p-7fe3a466.entry.js +1 -0
  117. package/dist/design-system/{p-05a1c092.entry.js → p-9466cd93.entry.js} +1 -1
  118. package/dist/design-system/{p-33bec0e3.entry.js → p-b683f2fe.entry.js} +1 -1
  119. package/dist/design-system/p-c521e731.entry.js +1 -0
  120. package/dist/design-system/p-c7bcb232.entry.js +1 -0
  121. package/dist/design-system/{p-16a15368.entry.js → p-c9eb70f5.entry.js} +1 -1
  122. package/dist/design-system/p-d1846df9.entry.js +1 -0
  123. package/dist/design-system/{p-2d154fe0.entry.js → p-fdb52620.entry.js} +1 -1
  124. package/dist/esm/design-system.js +1 -1
  125. package/dist/esm/index.js +5 -0
  126. package/dist/esm/loader.js +1 -1
  127. package/dist/esm/sd-button_4.entry.js +2 -2
  128. package/dist/esm/sd-confirm-modal_2.entry.js +1 -1
  129. package/dist/esm/sd-ghost-button.entry.js +10 -5
  130. package/dist/esm/sd-modal-container.entry.js +76 -68
  131. package/dist/esm/sd-pagination_5.entry.js +883 -0
  132. package/dist/esm/sd-radio-button.entry.js +6 -1
  133. package/dist/esm/sd-select-v2-list-item_4.entry.js +65 -5
  134. package/dist/esm/sd-switch.entry.js +1 -1
  135. package/dist/esm/sd-table.entry.js +168 -21
  136. package/dist/esm/sd-tabs.entry.js +1 -1
  137. package/dist/esm/sd-tag.entry.js +2 -2
  138. package/dist/esm/sd-td.entry.js +53 -1
  139. package/dist/esm/sd-text-link.entry.js +3 -3
  140. package/dist/esm/sd-textarea.entry.js +1 -1
  141. package/dist/esm/sd-toast-container.entry.js +1 -1
  142. package/dist/esm/sd-toast.entry.js +2 -2
  143. package/dist/esm/sd-toggle.entry.js +1 -1
  144. package/dist/types/components/sd-ghost-button/sd-ghost-button.d.ts +1 -0
  145. package/dist/types/components/sd-loading-modal/sd-loading-modal.d.ts +1 -1
  146. package/dist/types/components/sd-modal-container/sd-modal-container.config.d.ts +1 -1
  147. package/dist/types/components/sd-modal-container/sd-modal-container.d.ts +6 -4
  148. package/dist/types/components/sd-radio-button/sd-radio-button.d.ts +1 -0
  149. package/dist/types/components/sd-select-v2/sd-select-v2-listbox/sd-select-v2-listbox.d.ts +9 -0
  150. package/dist/types/components/sd-select-v2/sd-select-v2.d.ts +4 -0
  151. package/dist/types/components/sd-table/sd-table.d.ts +17 -0
  152. package/dist/types/components/sd-table/sd-td/sd-td.d.ts +8 -0
  153. package/dist/types/components/sd-table/sd-tr/sd-tr.d.ts +4 -0
  154. package/dist/types/components.d.ts +55 -3
  155. package/hydrate/index.js +482 -142
  156. package/hydrate/index.mjs +482 -142
  157. package/package.json +1 -1
  158. package/dist/cjs/sd-pagination_2.cjs.entry.js +0 -427
  159. package/dist/cjs/sd-tbody.cjs.entry.js +0 -66
  160. package/dist/cjs/sd-thead.cjs.entry.js +0 -179
  161. package/dist/cjs/sd-tr.cjs.entry.js +0 -171
  162. package/dist/components/p-Bbs5Ws0k.js +0 -1
  163. package/dist/components/p-Biplmgfa.js +0 -1
  164. package/dist/components/p-CgL8_FSD.js +0 -1
  165. package/dist/components/p-DuMkBStM.js +0 -1
  166. package/dist/components/p-vQDL-PZ8.js +0 -1
  167. package/dist/design-system/p-109a10e4.entry.js +0 -1
  168. package/dist/design-system/p-380198bc.entry.js +0 -1
  169. package/dist/design-system/p-6b537e2f.entry.js +0 -1
  170. package/dist/design-system/p-6e90fb80.entry.js +0 -1
  171. package/dist/design-system/p-7b77c65c.entry.js +0 -1
  172. package/dist/design-system/p-8f88bd67.entry.js +0 -1
  173. package/dist/design-system/p-ba5fea6f.entry.js +0 -1
  174. package/dist/design-system/p-be54d6bd.entry.js +0 -1
  175. package/dist/design-system/p-c3379a6e.entry.js +0 -1
  176. package/dist/design-system/p-dc07d618.entry.js +0 -1
  177. package/dist/design-system/p-ef09409c.entry.js +0 -1
  178. package/dist/design-system/p-f8237991.entry.js +0 -1
  179. package/dist/esm/sd-pagination_2.entry.js +0 -424
  180. package/dist/esm/sd-tbody.entry.js +0 -64
  181. package/dist/esm/sd-thead.entry.js +0 -177
  182. package/dist/esm/sd-tr.entry.js +0 -169
@@ -110,6 +110,11 @@ const SdRadioButton = class {
110
110
  disabled = false;
111
111
  name;
112
112
  change;
113
+ componentWillLoad() {
114
+ this.size ??= 'sm';
115
+ this.options ??= [];
116
+ this.disabled ??= false;
117
+ }
113
118
  handleRadioChange = (optionValue, optionDisabled) => {
114
119
  if (this.disabled || optionDisabled)
115
120
  return;
@@ -170,7 +175,7 @@ const SdRadioButton = class {
170
175
  '--sd-radio-button-content-select': RADIO_BUTTON_COLORS.content.select,
171
176
  '--sd-radio-button-content-disabled': RADIO_BUTTON_COLORS.content.disabled,
172
177
  };
173
- return (index.h("div", { key: '67c8e7ad03fb6ee4cabd06591e3e11e5d38914f0', class: this.getGroupClasses(), style: cssVars, role: "radiogroup", "aria-disabled": this.disabled.toString() }, this.options.map(option => {
178
+ return (index.h("div", { key: 'fbc0246d9cf615956121295e29bf8c702ee73edc', class: this.getGroupClasses(), style: cssVars, role: "radiogroup", "aria-disabled": this.disabled.toString() }, this.options.map(option => {
174
179
  const isSelected = this.isOptionSelected(option);
175
180
  const isDisabled = this.isOptionDisabled(option);
176
181
  return (index.h("label", { key: `radio-${option.value}`, class: this.getButtonClasses(option), "aria-label": option.label || 'radio option', "data-label": option.label }, index.h("input", { type: "radio", name: this.groupName, value: option.value.toString(), checked: isSelected, disabled: isDisabled, onInput: () => this.handleRadioChange(option.value, option.disabled) }), option.label && index.h("span", { class: "sd-radio-button__label" }, option.label)));
@@ -325,6 +325,8 @@ const SdSelectV2Listbox = class {
325
325
  options = [];
326
326
  value = null;
327
327
  emitValue = false;
328
+ useSearch = false;
329
+ useSelectAll = false;
328
330
  triggerWidth = '200px';
329
331
  maxWidth = '640px';
330
332
  maxHeight = '260px';
@@ -342,7 +344,40 @@ const SdSelectV2Listbox = class {
342
344
  get isMulti() {
343
345
  return this.type === 'multi' || this.type === 'multi_depth';
344
346
  }
347
+ static SELECT_ALL_OPTION = {
348
+ value: '__select_all__',
349
+ label: '전체',
350
+ };
351
+ get showSelectAll() {
352
+ return this.useSelectAll && this.isMulti;
353
+ }
354
+ getAllNonDisabledLeaves() {
355
+ const collect = (opts) => opts.flatMap(o => {
356
+ if (o.disabled)
357
+ return [];
358
+ if (o.children)
359
+ return collect(o.children);
360
+ return [o];
361
+ });
362
+ return collect(this.options);
363
+ }
364
+ get selectAllState() {
365
+ if (!this.showSelectAll)
366
+ return false;
367
+ const allLeaves = this.getAllNonDisabledLeaves();
368
+ if (allLeaves.length === 0)
369
+ return false;
370
+ const selectedValues = this.getSelectedValues();
371
+ const selectedCount = allLeaves.filter(l => selectedValues.has(l.value)).length;
372
+ if (selectedCount === 0)
373
+ return false;
374
+ if (selectedCount === allLeaves.length)
375
+ return true;
376
+ return null;
377
+ }
345
378
  get showSearch() {
379
+ if (!this.useSearch)
380
+ return false;
346
381
  const count = this.isDepth ? countLeaves(this.options) : this.options.length;
347
382
  return count >= SEARCH_THRESHOLD;
348
383
  }
@@ -444,6 +479,9 @@ const SdSelectV2Listbox = class {
444
479
  }
445
480
  get navigableOptions() {
446
481
  const items = [];
482
+ if (this.showSelectAll) {
483
+ items.push(SdSelectV2Listbox.SELECT_ALL_OPTION);
484
+ }
447
485
  const walk = (opts) => {
448
486
  for (const opt of opts) {
449
487
  const isGroup = !!opt.children;
@@ -457,6 +495,22 @@ const SdSelectV2Listbox = class {
457
495
  walk(this.filteredOptions);
458
496
  return items;
459
497
  }
498
+ isSelectAllOption(option) {
499
+ return this.showSelectAll && option.value === SdSelectV2Listbox.SELECT_ALL_OPTION.value;
500
+ }
501
+ emitSelectAll() {
502
+ if (!this.showSelectAll)
503
+ return;
504
+ const allLeaves = this.getAllNonDisabledLeaves();
505
+ this.optionSelect.emit({
506
+ option: { ...SdSelectV2Listbox.SELECT_ALL_OPTION, children: allLeaves },
507
+ leaves: allLeaves,
508
+ });
509
+ }
510
+ handleSelectAllClick = (e) => {
511
+ e.stopPropagation();
512
+ this.emitSelectAll();
513
+ };
460
514
  isOptionFocused(option) {
461
515
  if (this.focusedIndex < 0)
462
516
  return false;
@@ -529,7 +583,13 @@ const SdSelectV2Listbox = class {
529
583
  return;
530
584
  e.preventDefault();
531
585
  e.stopPropagation();
532
- this.emitOptionSelect(items[this.focusedIndex]);
586
+ const focused = items[this.focusedIndex];
587
+ if (this.isSelectAllOption(focused)) {
588
+ this.emitSelectAll();
589
+ }
590
+ else {
591
+ this.emitOptionSelect(focused);
592
+ }
533
593
  }
534
594
  };
535
595
  /**
@@ -608,9 +668,9 @@ const SdSelectV2Listbox = class {
608
668
  '--listbox-max-height': this.maxHeight,
609
669
  '--listbox-radius': `${LIST_BOX_LAYOUT.radius}px`,
610
670
  };
611
- return (index.h("div", { key: 'd4d80ebd565436cd929961a2f9ce50adf92c124b', class: "sd-select-v2-listbox", style: cssVars }, this.showSearch && (index.h("sd-select-v2-list-item-search", { key: 'bd549975fe6187aa7e77e718c8d2ba4a6d6e33de', isScrolled: this.isScrolled, onSdSearchFilter: this.handleSearchFilter })), index.h("div", { key: '4ac6a1512781942fef3e42d6ca32762962df1836', class: "sd-select-v2-listbox__list", onScroll: this.handleScroll, ref: el => {
671
+ return (index.h("div", { key: '34efe12dfb3829dd7024eb473d0bb6460ad07e61', class: "sd-select-v2-listbox", style: cssVars }, this.showSearch && (index.h("sd-select-v2-list-item-search", { key: 'd59a97386213e93f905a817b7c3242d1fb5d5035', isScrolled: this.isScrolled, onSdSearchFilter: this.handleSearchFilter })), index.h("div", { key: '0feb6763afe5ac661de85df2c1a3683e56b17035', class: "sd-select-v2-listbox__list", onScroll: this.handleScroll, ref: el => {
612
672
  this.listEl = el;
613
- } }, this.isEmpty ? (index.h("div", { class: "sd-select-v2-listbox__empty" }, EMPTY_MESSAGE)) : this.isDepth ? (this.renderOptions(this.filteredOptions)) : (this.filteredOptions.map(option => (index.h("sd-select-v2-list-item", { option: option, depth: 1, isSelected: this.isOptionSelected(option), isFocused: this.isOptionFocused(option), useCheckbox: this.isMulti, onSdListItemClick: this.handleOptionClick, onMouseEnter: () => this.handleOptionHover(option) })))))));
673
+ } }, this.showSelectAll && (index.h("sd-select-v2-list-item", { key: 'b4a765ddfadd39808baafd925d0cd2fcdab78800', option: SdSelectV2Listbox.SELECT_ALL_OPTION, depth: 1, isSelected: this.selectAllState, isFocused: this.isOptionFocused(SdSelectV2Listbox.SELECT_ALL_OPTION), useCheckbox: true, onSdListItemClick: this.handleSelectAllClick, onMouseEnter: () => this.handleOptionHover(SdSelectV2Listbox.SELECT_ALL_OPTION) })), this.isEmpty ? (index.h("div", { class: "sd-select-v2-listbox__empty" }, EMPTY_MESSAGE)) : this.isDepth ? (this.renderOptions(this.filteredOptions)) : (this.filteredOptions.map(option => (index.h("sd-select-v2-list-item", { option: option, depth: 1, isSelected: this.isOptionSelected(option), isFocused: this.isOptionFocused(option), useCheckbox: this.isMulti, onSdListItemClick: this.handleOptionClick, onMouseEnter: () => this.handleOptionHover(option) })))))));
614
674
  }
615
675
  static get watchers() { return {
616
676
  "searchKeyword": [{
@@ -670,13 +730,13 @@ const SdSelectV2Trigger = class {
670
730
  ? SELECT_COLORS.icon.disabled
671
731
  : SELECT_COLORS.icon.default,
672
732
  };
673
- return (index.h("div", { key: '7c4f60c972c17b5954cbf0d0cc7ee38c8e6c16a3', ref: el => {
733
+ return (index.h("div", { key: 'ec8bf544b2878082a046fbe881afa2369709033f', ref: el => {
674
734
  this.triggerEl = el;
675
735
  }, tabindex: this.disabled ? -1 : 0, class: {
676
736
  'sd-select-v2-trigger': true,
677
737
  'sd-select-v2-trigger--open': this.isOpen,
678
738
  'sd-select-v2-trigger--disabled': this.disabled,
679
- }, style: cssVars, onClick: this.handleClick, onFocus: this.handleFocus, onBlur: this.handleBlur }, index.h("div", { key: 'de5f5a44626fce29c5a83cf0a14dd03352d40b57', class: "sd-select-v2-trigger__content" }, index.h("span", { key: 'a36a013e9ef359160c2b9d24b42ee6f228aca302', class: "sd-select-v2-trigger__text" }, hasValue ? this.displayText : this.placeholder), index.h("sd-icon", { key: '4bfcfce96fbe2008517ee26d7802d4492292faa5', name: "chevronDown", size: 12, color: "var(--trigger-icon-color)", class: {
739
+ }, style: cssVars, onClick: this.handleClick, onFocus: this.handleFocus, onBlur: this.handleBlur }, index.h("div", { key: '1a6a5cf66208aa5a996e788886ff593ec0830d51', class: "sd-select-v2-trigger__content" }, index.h("span", { key: 'bd905f9149f3842e5e169e1a6086b33164b0ab66', class: "sd-select-v2-trigger__text" }, hasValue ? this.displayText : this.placeholder), index.h("sd-icon", { key: 'cc487e430f42fdd61540e265a9e5bb01e9045a58', name: "chevronDown", size: 12, color: "var(--trigger-icon-color)", class: {
680
740
  'sd-select-v2-trigger__icon': true,
681
741
  'sd-select-v2-trigger__icon--open': this.isOpen,
682
742
  } }))));
@@ -102,7 +102,7 @@ const SdSwitch = class {
102
102
  '--sd-switch-line-height': `${SWITCH_TYPOGRAPHY.lineHeight}px`,
103
103
  '--sd-switch-text-decoration': SWITCH_TYPOGRAPHY.textDecoration,
104
104
  };
105
- return (index.h("label", { key: 'ac6a35d06ddd04bc94369014ff586766dd745bbb', "aria-label": this.label || 'switch', class: this.switchClasses, style: cssVars }, index.h("input", { key: '9ebc810e93c9dfa3bf7407f279dadbada579196e', type: "checkbox", checked: this.value, disabled: this.disabled, onInput: this.handleChange }), index.h("div", { key: '7add76be99645d6434e95305f6318fac7da0de57', class: "sd-switch__track" }, index.h("div", { key: 'ed90778aa4dcd6f0853029579c36cfdb8640afcf', class: "sd-switch__knob" })), this.label && index.h("span", { key: '70e3ddf819e77da4c693a4853ddb7f392381964e', class: "sd-switch__label" }, this.label)));
105
+ return (index.h("label", { key: '469c012285d3c8a33792a460e74d8566c384efe8', "aria-label": this.label || 'switch', class: this.switchClasses, style: cssVars }, index.h("input", { key: '9678e3325339a47e3e2d81ce3cd752c86ed0f906', type: "checkbox", checked: this.value, disabled: this.disabled, onInput: this.handleChange }), index.h("div", { key: '47348914869f5215957a652cfcf3a11807a0216f', class: "sd-switch__track" }, index.h("div", { key: '0676260c42e6b79acec710f0f9ba72f01a3a7c18', class: "sd-switch__knob" })), this.label && index.h("span", { key: 'b92597092795bff38d2acf0cff76f9c381435438', class: "sd-switch__label" }, this.label)));
106
106
  }
107
107
  };
108
108
  SdSwitch.style = sdSwitchCss();
@@ -16,7 +16,7 @@ let nanoid = (size = 21) => {
16
16
  return id
17
17
  };
18
18
 
19
- const sdTableCss = () => `sd-table,:host{display:block;width:100%;max-width:100%;min-width:0}sd-table *,:host *{box-sizing:border-box}.sd-table__container{height:var(--table-height, auto);width:var(--table-width, 100%);max-width:100%;min-width:0;color:#222222;display:flex;flex-direction:column}.sd-table__clip{width:100%;min-width:0;height:var(--table-container-height, 400px);border:1px solid #E1E1E1;border-radius:8px;overflow:hidden}.sd-table__clip--has-pagination{border-radius:8px 8px 0 0}.sd-table__wrapper{width:100%;height:100%;display:flex;flex-direction:column;position:relative;font-size:12px;overflow:auto;background:#FFFFFF}.sd-table__wrapper--loading{overflow:hidden !important;pointer-events:none}.sd-table__wrapper--no-data{overflow:hidden;pointer-events:none}.sd-table__no-data{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:12px;color:#888888;pointer-events:none;z-index:200;background:rgba(255, 255, 255, 0.6)}.sd-table__loading{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(255, 255, 255, 0.6);z-index:200;display:flex;align-items:center;justify-content:center;pointer-events:none}.sd-table{background-color:white;display:table;width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed}.sd-table--selectable sd-thead,.sd-table--selectable sd-tbody{--selectable:true}.sd-table--sticky-header sd-thead thead{position:sticky;top:0;z-index:120}.sd-table--sticky-column sd-thead,.sd-table--sticky-column sd-tbody{--sticky-column:true}.sd-table--scrolled-left sd-thead,.sd-table--scrolled-left sd-tbody{--scrolled-left:true}.sd-table--scrolled-right sd-thead,.sd-table--scrolled-right sd-tbody{--scrolled-right:true}.sd-table--resizable sd-thead{--resizable:true}.sd-table--no-data sd-thead{opacity:0.4}.sd-table__pagination{position:relative;background:#F9F9F9;height:48px;display:flex;align-items:center;justify-content:center;border:1px solid #E1E1E1;border-top:none;border-radius:0 0 8px 8px}.sd-table__pagination sd-select-v2{position:absolute;right:10px;top:50%;transform:translateY(-50%)}`;
19
+ const sdTableCss = () => `sd-table,:host{display:block;width:100%;max-width:100%;min-width:0}sd-table *,:host *{box-sizing:border-box}.sd-table__container{height:var(--table-height, 100%);width:var(--table-width, 100%);max-width:100%;min-width:0;color:#222222;display:flex;flex-direction:column}.sd-table__clip{width:100%;min-width:0;height:var(--table-container-height, 400px);border:1px solid #E1E1E1;border-radius:8px;overflow:hidden}.sd-table__clip--has-pagination{border-radius:8px 8px 0 0}.sd-table__wrapper{width:100%;height:100%;display:flex;flex-direction:column;position:relative;font-size:12px;overflow:auto;background:#FFFFFF}.sd-table__wrapper--loading{overflow:hidden !important;pointer-events:none}.sd-table__wrapper--no-data{overflow:hidden;pointer-events:none}.sd-table__no-data{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:12px;color:#888888;pointer-events:none;z-index:200;background:rgba(255, 255, 255, 0.6)}.sd-table__loading{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(255, 255, 255, 0.6);z-index:200;display:flex;align-items:center;justify-content:center;pointer-events:none}.sd-table{background-color:white;display:table;width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed}.sd-table--selectable sd-thead,.sd-table--selectable sd-tbody{--selectable:true}.sd-table--sticky-header sd-thead thead{position:sticky;top:0;z-index:120}.sd-table--sticky-column sd-thead,.sd-table--sticky-column sd-tbody{--sticky-column:true}.sd-table--scrolled-left sd-thead,.sd-table--scrolled-left sd-tbody{--scrolled-left:true}.sd-table--scrolled-right sd-thead,.sd-table--scrolled-right sd-tbody{--scrolled-right:true}.sd-table--resizable sd-thead{--resizable:true}.sd-table--no-data sd-thead{opacity:0.4}.sd-table__pagination{position:relative;background:#F9F9F9;height:48px;display:flex;align-items:center;justify-content:center;border:1px solid #E1E1E1;border-top:none;border-radius:0 0 8px 8px}.sd-table__pagination sd-select-v2{position:absolute;right:10px;top:50%;transform:translateY(-50%)}`;
20
20
 
21
21
  const SdTable = class {
22
22
  constructor(hostRef) {
@@ -72,11 +72,17 @@ const SdTable = class {
72
72
  scrolledRight = false;
73
73
  rowCount = 0;
74
74
  loadingScrollTop = 0;
75
+ // light DOM에 sd-thead / sd-tbody 자식이 없으면 sd-table이 직접 렌더해야 함을 알리는 플래그.
76
+ // componentWillLoad에서 한 번 결정되며, 이후 동적 토글은 지원하지 않는다.
77
+ autoThead = false;
78
+ autoTbody = false;
75
79
  vsStart = 0;
76
80
  vsEnd = 0;
77
81
  lastReachEndNotifiedRowCount = -1;
78
82
  scrollContainer = null;
79
83
  onScroll;
84
+ // 키: `${rowKey}::${field}` → { rowspan, colspan }
85
+ spanRegistry = new Map();
80
86
  toFiniteNumber(value, fallback) {
81
87
  const n = typeof value === 'number' ? value : Number(value);
82
88
  return Number.isFinite(n) ? n : fallback;
@@ -155,9 +161,16 @@ const SdTable = class {
155
161
  this.innerRowsPerPage = newVal.rowsPerPage;
156
162
  }
157
163
  }
164
+ detectChildren() {
165
+ const hasThead = !!this.el.querySelector(':scope > sd-thead');
166
+ const hasTbody = !!this.el.querySelector(':scope > sd-tbody');
167
+ this.autoThead = !hasThead;
168
+ this.autoTbody = !hasTbody;
169
+ }
158
170
  componentWillLoad() {
159
171
  this.syncTableIdAttribute();
160
172
  this.handleNoDataLabelChange(this.noDataLabel);
173
+ this.detectChildren();
161
174
  this.innerSelected = new Set(this.selected || []);
162
175
  this.columnWidths = (this.columns || []).map(c => parseInt(c.width || '120', 10));
163
176
  if (this.pagination?.page) {
@@ -180,6 +193,11 @@ const SdTable = class {
180
193
  el.getTableIdSync = () => this.getResolvedTableId();
181
194
  el.getVirtualScrollConfigSync = this.getVirtualScrollConfigSync.bind(this);
182
195
  el.calculateVisibleRange = this.calculateVisibleRange.bind(this);
196
+ el.registerSpanSync = this.registerSpanSync.bind(this);
197
+ el.unregisterSpanSync = this.unregisterSpanSync.bind(this);
198
+ el.getSpanSync = this.getSpanSync.bind(this);
199
+ el.isCoveredSync = this.isCoveredSync.bind(this);
200
+ el.hasRowspanSync = this.hasRowspanSync.bind(this);
183
201
  if (Array.isArray(this.rows)) {
184
202
  this.rowCount = this.rows.length;
185
203
  this.pushRowsToChildren(this.rows);
@@ -228,11 +246,22 @@ const SdTable = class {
228
246
  this.scrollContainer.removeEventListener('scroll', this.onScroll);
229
247
  }
230
248
  }
249
+ // light DOM(manual mode 자식)과 shadow DOM(autoThead/autoTbody fallback) 양쪽 모두에서 자식을 찾는다.
250
+ queryChildEl(selector) {
251
+ return (this.el.querySelector(selector) ??
252
+ this.el.shadowRoot?.querySelector(selector) ??
253
+ null);
254
+ }
255
+ queryAllTr() {
256
+ const light = Array.from(this.el.querySelectorAll('sd-tr'));
257
+ const shadow = Array.from(this.el.shadowRoot?.querySelectorAll('sd-tr') ?? []);
258
+ return [...light, ...shadow];
259
+ }
231
260
  pushRowsToChildren(rows) {
232
- const tbody = this.el.querySelector('sd-tbody');
261
+ const tbody = this.queryChildEl('sd-tbody');
233
262
  if (tbody)
234
263
  tbody.rows = rows;
235
- const thead = this.el.querySelector('sd-thead');
264
+ const thead = this.queryChildEl('sd-thead');
236
265
  if (thead)
237
266
  thead.rows = rows;
238
267
  }
@@ -243,16 +272,14 @@ const SdTable = class {
243
272
  this.refreshChildrenConfig();
244
273
  };
245
274
  refreshChildrenSelection() {
246
- const thead = this.el.querySelector('sd-thead');
247
- const rows = this.el.querySelectorAll('sd-tr');
275
+ const thead = this.queryChildEl('sd-thead');
248
276
  thead?.refreshSelection?.();
249
- rows.forEach(tr => tr?.refreshSelection?.());
277
+ this.queryAllTr().forEach(tr => tr?.refreshSelection?.());
250
278
  }
251
279
  refreshChildrenConfig() {
252
- const thead = this.el.querySelector('sd-thead');
253
- const rows = this.el.querySelectorAll('sd-tr');
280
+ const thead = this.queryChildEl('sd-thead');
254
281
  thead?.refreshConfig?.();
255
- rows.forEach(tr => tr?.refreshConfig?.());
282
+ this.queryAllTr().forEach(tr => tr?.refreshConfig?.());
256
283
  }
257
284
  maybeEmitVirtualReachEnd(start, end) {
258
285
  const threshold = Math.max(1, this.virtualEndThreshold);
@@ -284,7 +311,7 @@ const SdTable = class {
284
311
  this.vsEnd = end;
285
312
  const topHeight = start * this.rowHeight;
286
313
  const bottomHeight = Math.max(0, (this.rowCount - end) * this.rowHeight);
287
- const tbody = this.el.querySelector('sd-tbody');
314
+ const tbody = this.queryChildEl('sd-tbody');
288
315
  tbody?.setSpacersSync?.(topHeight, bottomHeight);
289
316
  if (rangeChanged) {
290
317
  this.sdVirtualUpdate.emit({
@@ -376,8 +403,7 @@ const SdTable = class {
376
403
  this.updateRowsVisibility();
377
404
  }
378
405
  updateRowsVisibility() {
379
- const rows = this.el.querySelectorAll('sd-tr');
380
- rows.forEach(tr => tr?.updateVisibility?.());
406
+ this.queryAllTr().forEach(tr => tr?.updateVisibility?.());
381
407
  }
382
408
  changeRowsPerPage(perPage) {
383
409
  const changedRowsPerPage = perPage ? Number(perPage) : 0;
@@ -414,10 +440,9 @@ const SdTable = class {
414
440
  const delta = moveEvent.clientX - startX;
415
441
  const newWidth = Math.min(Math.max(startWidth + (reversed ? -delta : delta), minWidth), maxWidth);
416
442
  this.columnWidths = this.columnWidths.map((width, idx) => (idx === index ? newWidth : width));
417
- const thead = this.el.querySelector('sd-thead');
418
- const rows = this.el.querySelectorAll('sd-tr');
443
+ const thead = this.queryChildEl('sd-thead');
419
444
  thead?.setColumnWidths?.(this.columnWidths);
420
- rows.forEach(tr => tr?.setColumnWidths?.(this.columnWidths));
445
+ this.queryAllTr().forEach(tr => tr?.setColumnWidths?.(this.columnWidths));
421
446
  const stickyRightCount = this.stickyColumn?.right || 0;
422
447
  const visibleColCount = this.columns.filter(c => c.visible !== false).length;
423
448
  const isRightStickyEdgeResizer = stickyRightCount > 0 && index === visibleColCount - stickyRightCount;
@@ -454,6 +479,112 @@ const SdTable = class {
454
479
  async getStickyStyle(colIdx) {
455
480
  return this.getStickyStyleSync(colIdx);
456
481
  }
482
+ // ─── rowspan / colspan registry ─────────────────────────────────
483
+ // sd-td가 mount/unmount 시 자기 (rowKey, field)와 span을 등록한다.
484
+ // sd-tr는 render마다 isCoveredSync로 자신의 셀 위치가 다른 셀의 span에
485
+ // 덮였는지 판정해 <td>를 그릴지 결정한다.
486
+ spanKey(rowKey, field) {
487
+ return `${rowKey}::${field}`;
488
+ }
489
+ // span 등록은 sd-td의 lifecycle에서 비동기적으로 일어나므로,
490
+ // 등록/해제 직후 형제 sd-tr들이 새 레지스트리 상태로 다시 그려져야
491
+ // 덮인 셀이 사라지거나 다시 나타난다.
492
+ // forceUpdate는 React 래퍼 환경에서 prop 동기화 사이클과 부딪혀 누락되는
493
+ // 경우가 있어, sd-tr의 @State (spansVersion)을 통해 재렌더를 강제한다.
494
+ requestAllTrUpdate() {
495
+ this.queryAllTr().forEach(tr => {
496
+ const trAny = tr;
497
+ if (typeof trAny.bumpSpansVersion === 'function') {
498
+ trAny.bumpSpansVersion();
499
+ }
500
+ else {
501
+ index.forceUpdate(tr);
502
+ }
503
+ });
504
+ }
505
+ registerSpanSync(rowKey, field, rowspan, colspan) {
506
+ if (rowKey == null || !field)
507
+ return;
508
+ const safeRowspan = Math.max(1, Math.floor(rowspan || 1));
509
+ const safeColspan = Math.max(1, Math.floor(colspan || 1));
510
+ const key = this.spanKey(rowKey, field);
511
+ const prev = this.spanRegistry.get(key);
512
+ if (safeRowspan === 1 && safeColspan === 1) {
513
+ if (!prev)
514
+ return;
515
+ this.spanRegistry.delete(key);
516
+ this.requestAllTrUpdate();
517
+ return;
518
+ }
519
+ if (prev && prev.rowspan === safeRowspan && prev.colspan === safeColspan)
520
+ return;
521
+ this.spanRegistry.set(key, { rowspan: safeRowspan, colspan: safeColspan });
522
+ this.requestAllTrUpdate();
523
+ }
524
+ unregisterSpanSync(rowKey, field) {
525
+ if (rowKey == null || !field)
526
+ return;
527
+ const key = this.spanKey(rowKey, field);
528
+ if (!this.spanRegistry.has(key))
529
+ return;
530
+ this.spanRegistry.delete(key);
531
+ this.requestAllTrUpdate();
532
+ }
533
+ getSpanSync(rowKey, field) {
534
+ return this.spanRegistry.get(this.spanKey(rowKey, field));
535
+ }
536
+ // 레지스트리에 rowspan>1 항목이 하나라도 있으면 true.
537
+ // hover 동작을 끌지 결정하는 데 사용 — colspan만 있는 경우는 그대로 hover 유지.
538
+ hasRowspanSync() {
539
+ for (const span of this.spanRegistry.values()) {
540
+ if (span.rowspan > 1)
541
+ return true;
542
+ }
543
+ return false;
544
+ }
545
+ isCoveredSync(rowKey, colIdx, columns) {
546
+ if (this.spanRegistry.size === 0)
547
+ return false;
548
+ const visibleCols = columns.filter(c => c.visible !== false);
549
+ // 1. 같은 행 왼쪽 스캔 — colspan으로 이 위치를 덮는 셀이 있는가
550
+ for (let i = 0; i < colIdx; i++) {
551
+ const c = visibleCols[i];
552
+ if (!c)
553
+ continue;
554
+ const field = typeof c.field === 'string' ? c.field : c.name;
555
+ const span = this.spanRegistry.get(this.spanKey(rowKey, field));
556
+ if (!span)
557
+ continue;
558
+ if (i + span.colspan > colIdx)
559
+ return true;
560
+ }
561
+ // 2. 위쪽 행 스캔 — 숫자 변환 가능한 rowKey만 rowspan 평가
562
+ const myRowIdx = Number(rowKey);
563
+ if (!Number.isFinite(myRowIdx))
564
+ return false;
565
+ for (const [key, span] of this.spanRegistry) {
566
+ if (span.rowspan <= 1)
567
+ continue;
568
+ const sepIdx = key.indexOf('::');
569
+ if (sepIdx < 0)
570
+ continue;
571
+ const otherRowKey = key.slice(0, sepIdx);
572
+ const otherField = key.slice(sepIdx + 2);
573
+ const otherRowIdx = Number(otherRowKey);
574
+ if (!Number.isFinite(otherRowIdx))
575
+ continue;
576
+ if (otherRowIdx >= myRowIdx)
577
+ continue;
578
+ if (otherRowIdx + span.rowspan <= myRowIdx)
579
+ continue;
580
+ const otherColIdx = visibleCols.findIndex(c => (typeof c.field === 'string' ? c.field : c.name) === otherField);
581
+ if (otherColIdx < 0)
582
+ continue;
583
+ if (otherColIdx <= colIdx && otherColIdx + span.colspan > colIdx)
584
+ return true;
585
+ }
586
+ return false;
587
+ }
457
588
  setRowCountSync(count) {
458
589
  const safeCount = Math.max(0, Math.floor(this.toFiniteNumber(count, 0)));
459
590
  if (safeCount !== this.rowCount) {
@@ -501,6 +632,22 @@ const SdTable = class {
501
632
  return null;
502
633
  return { from: this.vsStart, to: this.vsEnd };
503
634
  }
635
+ // autoTbody fallback에서 sd-table이 직접 sd-tr을 만들어내는 경로.
636
+ // 가상 스크롤은 사용자가 직접 SdTbody+SdTr을 작성해야 하므로 빈 배열을 반환한다.
637
+ renderAutoRows() {
638
+ if (this.useVirtualScroll)
639
+ return null;
640
+ const allRows = this.rows ?? [];
641
+ const pageInfo = this.getPaginationInfoSync();
642
+ const startIdx = pageInfo?.startIndex ?? 0;
643
+ const displayed = pageInfo
644
+ ? allRows.slice(pageInfo.startIndex, pageInfo.endIndex)
645
+ : allRows;
646
+ return displayed.map((row, i) => {
647
+ const absoluteIdx = startIdx + i;
648
+ return (index.h("sd-tr", { key: absoluteIdx, "row-key": String(absoluteIdx), row: row }));
649
+ });
650
+ }
504
651
  get tableClasses() {
505
652
  return [
506
653
  'sd-table',
@@ -520,24 +667,24 @@ const SdTable = class {
520
667
  }
521
668
  render() {
522
669
  const resolvedTableId = this.getResolvedTableId();
523
- return (index.h(index.Host, { key: '0b90643721a90b7bb59a6c31f6edeb313849b973' }, index.h("div", { key: 'e55fe4f476dbea4888a168861e7e6d6951d7d489', class: "sd-table__container", style: {
670
+ return (index.h(index.Host, { key: 'd73cd690ad11ce92af37b6f32374f6f891c5b677' }, index.h("div", { key: 'f51d23212885ad8121b9a4e895fb854f1e142bc4', class: "sd-table__container", style: {
524
671
  '--table-width': this.width,
525
672
  '--table-height': this.height,
526
673
  '--table-container-height': `calc(${this.height || '100%'} - ${this.pagination && this.rowCount > 0 && !this.useVirtualScroll ? 48 : 0}px)`,
527
- } }, index.h("div", { key: 'de18a900037a7b2f619dd22981c30ed76f5a8111', class: {
674
+ } }, index.h("div", { key: '84b1ba7b2220ff55304b9c19e59304ca2257cff0', class: {
528
675
  'sd-table__clip': true,
529
676
  'sd-table__clip--has-pagination': !!(this.pagination &&
530
677
  this.pagination.rowsPerPage > 0 &&
531
678
  this.rowCount > 0 &&
532
679
  !this.useVirtualScroll),
533
- } }, index.h("div", { key: '028e0982d415033adaf420ee6b953241c8f3dbec', class: {
680
+ } }, index.h("div", { key: 'c901eba67eae29515bf0b3edcc6632b2aacf0f80', class: {
534
681
  'sd-table__wrapper': true,
535
682
  'sd-table__wrapper--loading': this.isLoading,
536
683
  'sd-table__wrapper--no-data': this.rowCount === 0 && !this.isLoading,
537
- } }, this.isLoading && (index.h("div", { key: '8c9110c9c2c26cddbbed8e1f2782ecfaafd3679c', class: "sd-table__loading", style: { top: `${this.loadingScrollTop}px` } }, index.h("sd-circle-progress", { key: '51b61f91b0ecebf63fe3e65b94012963f0375e5c', indeterminate: true }))), this.rowCount === 0 && !this.isLoading && (index.h("div", { key: '0a7a45049014869b8164b081a251218d3c814e87', class: "sd-table__no-data" }, index.h("slot", { key: 'd4b6219e5dcaaa22be3360f7f0f05a8b32533890', name: "no-data" }, index.h("span", { key: 'c76848aad8a1d3967f3cd239090e97266e46ab3e' }, this.resolvedNoDataLabel)))), index.h("table", { key: '2b560d112402549c7043b337d944b0de88c3ba0e', class: this.tableClasses }, index.h("slot", { key: '1c4583148df6a585f389c1bed0426fa4a3cb4899', name: `${resolvedTableId}-head`, onSlotchange: this.handleStructureSlotChange }), index.h("slot", { key: 'a5d983106de61c829f9cbeb541d1908fee48468c', name: `${resolvedTableId}-body`, onSlotchange: this.handleStructureSlotChange })))), this.pagination &&
684
+ } }, this.isLoading && (index.h("div", { key: '35a237d0203b2479dbdb77ac42c918a9375bdfd3', class: "sd-table__loading", style: { top: `${this.loadingScrollTop}px` } }, index.h("sd-circle-progress", { key: '21a29981c1cdbd679f46abd2e7de7794c9ebbca9', indeterminate: true }))), this.rowCount === 0 && !this.isLoading && (index.h("div", { key: '07ad28bc6e7556cfe229fc1e952410f388424a7f', class: "sd-table__no-data" }, index.h("slot", { key: 'bf21e60f5b86614587b704bea2965b9467c7f467', name: "no-data" }, index.h("span", { key: 'f3d012d12e9189545b0cef52250502d46cb9a764' }, this.resolvedNoDataLabel)))), index.h("table", { key: '655d3dc017c6445ec454faef0e5e9837b7ee0013', class: this.tableClasses }, this.autoThead ? (index.h("slot", { name: `${resolvedTableId}-head`, onSlotchange: this.handleStructureSlotChange }, index.h("sd-thead", { rows: this.rows ?? [] }))) : (index.h("slot", { name: `${resolvedTableId}-head`, onSlotchange: this.handleStructureSlotChange })), this.autoTbody ? (index.h("slot", { name: `${resolvedTableId}-body`, onSlotchange: this.handleStructureSlotChange }, index.h("sd-tbody", { rows: this.rows ?? [] }, this.renderAutoRows()))) : (index.h("slot", { name: `${resolvedTableId}-body`, onSlotchange: this.handleStructureSlotChange }))))), this.pagination &&
538
685
  this.pagination.rowsPerPage > 0 &&
539
686
  this.rowCount > 0 &&
540
- !this.useVirtualScroll && (index.h("div", { key: '004355d84e2cfc2fd38a9d4811f14fd66dc5c21b', class: "sd-table__pagination" }, index.h("sd-pagination", { key: '463586c7e57782a7989962f73cb3d4e8c04f5bce', currentPage: !this.useInternalPagination ? this.pagination.page : this.currentPage, lastPage: !this.useInternalPagination ? this.pagination.lastPage : this.lastPageNumber, onSdPageChange: (e) => this.changePage(e.detail) }), this.useRowsPerPageSelect && (index.h("sd-select-v2", { key: '1f26de447da63ef0c4a24ffb49666a0687db9991', value: this.useInternalPagination
687
+ !this.useVirtualScroll && (index.h("div", { key: '7ab0b30a0c0e0a197b0f79c6b07ef5614d5d2879', class: "sd-table__pagination" }, index.h("sd-pagination", { key: '71d44bba5a82f4d8f7c067e525db15fe9a36c305', currentPage: !this.useInternalPagination ? this.pagination.page : this.currentPage, lastPage: !this.useInternalPagination ? this.pagination.lastPage : this.lastPageNumber, onSdPageChange: (e) => this.changePage(e.detail) }), this.useRowsPerPageSelect && (index.h("sd-select-v2", { key: 'a9ab33347db0714da188f658a2ca1902502db690', value: this.useInternalPagination
541
688
  ? this.innerRowsPerPage
542
689
  : this.pagination.rowsPerPage, options: this.rowsPerPageOption, width: "128px", emitValue: true, onSdUpdate: e => {
543
690
  if (!this.isRowsPerPageValue(e.detail))
@@ -188,7 +188,7 @@ const SdTabs = class {
188
188
  };
189
189
  }
190
190
  render() {
191
- return (index.h("div", { key: '390edfad274caa06df38af9d04580015a1744a44', class: this.getContainerClasses(), style: this.buildCssVars() }, this.tabs.map((tab, index$1) => {
191
+ return (index.h("div", { key: '3698b7b43e74ff053d61d5ce696b987373fd27d6', class: this.getContainerClasses(), style: this.buildCssVars() }, this.tabs.map((tab, index$1) => {
192
192
  const badgeName = this.getBadgeName(tab);
193
193
  return (index.h("div", { key: `tab-${index$1}`, class: this.getTabClasses(tab), "aria-label": tab.label || 'tab', onClick: () => this.handleTabClick(tab) }, index.h("span", { "data-label": tab.label, class: "sd-tabs__label" }, tab.label), tab.badge !== undefined && tab.badge !== null && tab.badge !== '' && (index.h("sd-tag", { name: badgeName, label: tab.badge.toString() }))));
194
194
  })));
@@ -172,7 +172,7 @@ const SdTag = class {
172
172
  render() {
173
173
  const config = this.resolvedConfig;
174
174
  const iconNode = this.renderIcon(config.icon, config.iconSize);
175
- return (index.h("span", { key: 'da19900d267d2b2f000e1790133966a7976bcded', class: "sd-tag", style: {
175
+ return (index.h("span", { key: 'def10730670f37a62fa76b507f7f15a1b1f956fc', class: "sd-tag", style: {
176
176
  '--sd-tag-background': config.background,
177
177
  '--sd-tag-content': config.content,
178
178
  '--sd-tag-height': config.height,
@@ -182,7 +182,7 @@ const SdTag = class {
182
182
  '--sd-tag-font-weight': config.fontWeight,
183
183
  '--sd-tag-line-height': config.lineHeight,
184
184
  '--sd-tag-radius': config.radius,
185
- }, "aria-label": this.label || 'tag' }, this.icon && this.isLeft && iconNode, index.h("span", { key: 'e426021988395058367872ac23658586b2895764', class: "sd-tag__label" }, this.label), this.icon && !this.isLeft && iconNode));
185
+ }, "aria-label": this.label || 'tag' }, this.icon && this.isLeft && iconNode, index.h("span", { key: '32082536736e20074263632aa29f36bc7709db39', class: "sd-tag__label" }, this.label), this.icon && !this.isLeft && iconNode));
186
186
  }
187
187
  };
188
188
  SdTag.style = sdTagCss();
@@ -13,14 +13,22 @@ const SdTd = class {
13
13
  field;
14
14
  rowKey;
15
15
  align;
16
+ rowspan;
17
+ colspan;
16
18
  handleFieldChange() {
17
19
  this.syncSlotName();
20
+ this.syncSpanRegistration();
18
21
  }
19
22
  handleRowKeyChange() {
20
23
  this.syncSlotName();
24
+ this.syncSpanRegistration();
25
+ }
26
+ handleSpanChange() {
27
+ this.syncSpanRegistration();
21
28
  }
22
29
  componentWillLoad() {
23
30
  this.syncSlotName();
31
+ this.syncSpanRegistration();
24
32
  // slot 타이밍 엇갈림 대응: 부모 sd-tr forceUpdate로 슬롯 재매칭
25
33
  const parentTr = this.el.parentElement;
26
34
  if (parentTr?.tagName?.toLowerCase() === 'sd-tr') {
@@ -29,6 +37,44 @@ const SdTd = class {
29
37
  }
30
38
  componentDidLoad() {
31
39
  this.syncSlotName();
40
+ this.syncSpanRegistration();
41
+ }
42
+ // React StrictMode에서는 disconnect/reconnect 사이클이 일어나면서
43
+ // 동일 인스턴스의 componentWillLoad는 더 이상 호출되지 않는다.
44
+ // 재연결 시점에도 등록 상태를 복구해야 rowspan/colspan이 유지된다.
45
+ connectedCallback() {
46
+ this.syncSpanRegistration();
47
+ }
48
+ disconnectedCallback() {
49
+ const table = this.findTable();
50
+ if (table?.unregisterSpanSync && this.field && this.rowKey != null) {
51
+ table.unregisterSpanSync(String(this.rowKey), this.field);
52
+ this.requestParentTrUpdate();
53
+ }
54
+ }
55
+ findTable() {
56
+ return this.el.closest('sd-table');
57
+ }
58
+ requestParentTrUpdate() {
59
+ const parentTr = this.el.parentElement;
60
+ if (parentTr?.tagName?.toLowerCase() !== 'sd-tr')
61
+ return;
62
+ const trAny = parentTr;
63
+ if (typeof trAny.bumpSpansVersion === 'function') {
64
+ trAny.bumpSpansVersion();
65
+ }
66
+ else {
67
+ index.forceUpdate(parentTr);
68
+ }
69
+ }
70
+ syncSpanRegistration() {
71
+ const table = this.findTable();
72
+ if (!table?.registerSpanSync || !this.field || this.rowKey == null)
73
+ return;
74
+ const rs = Math.max(1, Math.floor(Number(this.rowspan) || 1));
75
+ const cs = Math.max(1, Math.floor(Number(this.colspan) || 1));
76
+ table.registerSpanSync(String(this.rowKey), this.field, rs, cs);
77
+ this.requestParentTrUpdate();
32
78
  }
33
79
  syncSlotName() {
34
80
  const table = this.el.closest('sd-table');
@@ -42,7 +88,7 @@ const SdTd = class {
42
88
  }
43
89
  }
44
90
  render() {
45
- return (index.h(index.Host, { key: '672c967273dac405ed4a47fa5939463265075681', class: { [`align-${this.align}`]: Boolean(this.align) } }, index.h("slot", { key: 'c15e572fdf4a8c68fff8b69b586dfbf9f01dce1b' })));
91
+ return (index.h(index.Host, { key: 'da9ce2edb986d4b3cf1a6e5f59030009f1288250', class: { [`align-${this.align}`]: Boolean(this.align) } }, index.h("slot", { key: '8514071bd38c4f5b1997ae7239b8585a25f97ce0' })));
46
92
  }
47
93
  static get watchers() { return {
48
94
  "field": [{
@@ -50,6 +96,12 @@ const SdTd = class {
50
96
  }],
51
97
  "rowKey": [{
52
98
  "handleRowKeyChange": 0
99
+ }],
100
+ "rowspan": [{
101
+ "handleSpanChange": 0
102
+ }],
103
+ "colspan": [{
104
+ "handleSpanChange": 0
53
105
  }]
54
106
  }; }
55
107
  };
@@ -73,16 +73,16 @@ const SdTextLink = class {
73
73
  '--sd-text-link-text-decoration': typo.textDecoration,
74
74
  ...(this.disabled ? { '--sd-text-link-color': TEXT_LINK_COLORS.content.disabled } : {}),
75
75
  };
76
- return (index.h("span", { key: '1128254cddacf5869f320ce2b788b06646f27d7f', class: {
76
+ return (index.h("span", { key: '77c1aa4a4a6297af9431947b7146db2ddfad52b5', class: {
77
77
  'sd-text-link': true,
78
78
  'sd-text-link--disabled': this.disabled,
79
- }, style: cssVars, onClick: this.handleClick }, this.icon && (index.h("sd-icon", { key: '52c069d637a2ccf9cd4e7bd1ab3a62ea981d2e1e', name: this.icon, size: TEXT_LINK_LAYOUT.iconSize, color: iconColor, class: "sd-text-link__icon" })), index.h("span", { key: '99a09359c75c2eb6c2e2ef6b9e3b87d92c3f7810', class: labelClassName }, this.label), this.useArrow && (index.h("span", { key: 'c7e77454d9d002c0e043dc71a418c65ecd60cff5', class: "sd-text-link__arrow", style: {
79
+ }, style: cssVars, onClick: this.handleClick }, this.icon && (index.h("sd-icon", { key: '6f760b3c7e0adccd2aa08cb662cca90a3d3a227b', name: this.icon, size: TEXT_LINK_LAYOUT.iconSize, color: iconColor, class: "sd-text-link__icon" })), index.h("span", { key: '3ff017970956d8e5cb72b4d6e26d33aaf0551e76', class: labelClassName }, this.label), this.useArrow && (index.h("span", { key: '8332cbe3b2283ce866044e55804e35338569a38f', class: "sd-text-link__arrow", style: {
80
80
  width: `${TEXT_LINK_LAYOUT.arrowFrame}px`,
81
81
  height: `${TEXT_LINK_LAYOUT.arrowFrame}px`,
82
82
  display: 'inline-flex',
83
83
  alignItems: 'center',
84
84
  justifyContent: 'center',
85
- } }, index.h("sd-icon", { key: '677c88823b02084449cd2dec83cc6bd27dfb8006', name: "chevronRight", size: TEXT_LINK_LAYOUT.arrowIconSize, color: arrowColor })))));
85
+ } }, index.h("sd-icon", { key: '2fa166ef60f6d1ee933450146bc667ee89dad2ce', name: "chevronRight", size: TEXT_LINK_LAYOUT.arrowIconSize, color: arrowColor })))));
86
86
  }
87
87
  };
88
88
  SdTextLink.style = sdTextLinkCss();
@@ -130,7 +130,7 @@ const SdTextarea = class {
130
130
  '--sd-system-size-field-sm-height': 'auto',
131
131
  '--sd-system-radius-field-sm': `${TEXTAREA_TOKENS.radius}px`,
132
132
  };
133
- return (index.h("sd-field", { key: '345a70f335ee103a4e45c75e17559359beec01c9', name: this.name, label: this.label, labelWidth: this.labelWidth, addonLabel: this.addonLabel, addonAlign: this.addonAlign, hint: this.hint, errorMessage: this.errorMessage, width: this.width, rules: this.rules, error: this.error, disabled: this.disabled, focused: this.focused, hovered: this.hovered, status: this.status, icon: this.icon, labelTooltip: this.labelTooltip, labelTooltipProps: this.labelTooltipProps, ref: el => (this.formField = el), onMouseEnter: () => (this.hovered = true), onMouseLeave: () => (this.hovered = false), style: cssVars }, index.h("div", { key: 'dc09d61d06f3cefb15dce980e293a266fdf3d79d', class: "sd-textarea__content" }, index.h("textarea", { key: '085de248d7376e0d19d3588bc836bd5ad17cb335', name: this.name, ref: el => (this.nativeEl = el), class: `sd-textarea__native ${this.textareaClass}`, value: this.internalValue || '', placeholder: this.placeholder, disabled: this.disabled, readOnly: this.readonly, autofocus: this.autoFocus, maxLength: this.maxLength, rows: this.rows, spellcheck: this.spellcheck, onInput: this.handleInput, onFocus: event => this.handleFocus('focus', event), onBlur: event => this.handleFocus('blur', event), style: this.textareaStyle }))));
133
+ return (index.h("sd-field", { key: 'a77834af45e2ca7abaa019d18b6108a51f0cf4ae', name: this.name, label: this.label, labelWidth: this.labelWidth, addonLabel: this.addonLabel, addonAlign: this.addonAlign, hint: this.hint, errorMessage: this.errorMessage, width: this.width, rules: this.rules, error: this.error, disabled: this.disabled, focused: this.focused, hovered: this.hovered, status: this.status, icon: this.icon, labelTooltip: this.labelTooltip, labelTooltipProps: this.labelTooltipProps, ref: el => (this.formField = el), onMouseEnter: () => (this.hovered = true), onMouseLeave: () => (this.hovered = false), style: cssVars }, index.h("div", { key: '2cbc8ef4febeab89eb336cb29d34abdbf9f2f080', class: "sd-textarea__content" }, index.h("textarea", { key: '3b09e30b4e42affc7f03fb8a354387b57e5ee0fe', name: this.name, ref: el => (this.nativeEl = el), class: `sd-textarea__native ${this.textareaClass}`, value: this.internalValue || '', placeholder: this.placeholder, disabled: this.disabled, readOnly: this.readonly, autofocus: this.autoFocus, maxLength: this.maxLength, rows: this.rows, spellcheck: this.spellcheck, onInput: this.handleInput, onFocus: event => this.handleFocus('focus', event), onBlur: event => this.handleFocus('blur', event), style: this.textareaStyle }))));
134
134
  }
135
135
  static get watchers() { return {
136
136
  "value": [{
@@ -234,7 +234,7 @@ const SdToastContainer = class {
234
234
  const activeToasts = toasts.filter(t => t.state !== 'exiting').reverse();
235
235
  const indexMap = new Map();
236
236
  activeToasts.forEach((t, i) => indexMap.set(t.id, i));
237
- return (index.h("div", { key: '601c4c5aa55df02459866607ca7c59da53c2eda5', class: "sd-toast-container", style: this.getContainerStyles(), onMouseEnter: () => {
237
+ return (index.h("div", { key: '5057793be2d6ce213edc84d58f7273248646dc19', class: "sd-toast-container", style: this.getContainerStyles(), onMouseEnter: () => {
238
238
  this.expanded = true;
239
239
  this.pauseTimers();
240
240
  }, onMouseLeave: () => {