@sellmate/design-system 1.21.0 → 1.22.0

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 (105) hide show
  1. package/dist/cjs/design-system.cjs.js +1 -1
  2. package/dist/cjs/loader.cjs.js +1 -1
  3. package/dist/cjs/sd-button_2.cjs.entry.js +4 -4
  4. package/dist/cjs/sd-chip-input.cjs.entry.js +41 -17
  5. package/dist/cjs/sd-pagination_4.cjs.entry.js +86 -7
  6. package/dist/collection/components/assets/CaretDown.js +1 -1
  7. package/dist/collection/components/assets/CaretLeft.js +1 -1
  8. package/dist/collection/components/assets/CaretRight.js +1 -1
  9. package/dist/collection/components/assets/CaretUp.js +1 -1
  10. package/dist/collection/components/sd-chip-input/sd-chip-input.css +5 -0
  11. package/dist/collection/components/sd-chip-input/sd-chip-input.js +40 -16
  12. package/dist/collection/components/sd-table/sd-table.stories.helpers.js +97 -0
  13. package/dist/collection/components/sd-table/sd-thead/sd-thead.css +21 -0
  14. package/dist/collection/components/sd-table/sd-thead/sd-thead.js +87 -7
  15. package/dist/components/{p-D4zy1T3V.js → p-58DLP3IW.js} +1 -1
  16. package/dist/components/{p-CcwzTAJC.js → p-B0myARGe.js} +1 -1
  17. package/dist/components/{p-CBDBmMmw.js → p-B12iH3Zk.js} +1 -1
  18. package/dist/components/{p-B5VNSTAf.js → p-B2bF2KlS.js} +1 -1
  19. package/dist/components/{p-C3mTIE8H.js → p-BXRgi3NZ.js} +1 -1
  20. package/dist/components/{p-DC5W8Uy1.js → p-Bm3NWPaP.js} +1 -1
  21. package/dist/components/{p-FA9fGvbL.js → p-BpQwjSB8.js} +1 -1
  22. package/dist/components/{p-BjSMh85k.js → p-BpjmheLG.js} +1 -1
  23. package/dist/components/{p-CTw1Deo8.js → p-Bric-SEs.js} +1 -1
  24. package/dist/components/{p-CZYTZeMe.js → p-BwTsP0Ak.js} +1 -1
  25. package/dist/components/p-CCO1WtEj.js +1 -0
  26. package/dist/components/{p-DPXOBYhf.js → p-CHxVi6l1.js} +1 -1
  27. package/dist/components/{p-Dz7VXmIA.js → p-Cdzn78dP.js} +1 -1
  28. package/dist/components/{p-YpyfqzoP.js → p-Cjf8PHEl.js} +1 -1
  29. package/dist/components/{p-l7TaS8q9.js → p-Co2ThnbG.js} +1 -1
  30. package/dist/components/{p-BrHnpCXR.js → p-CwSlRPMm.js} +1 -1
  31. package/dist/components/{p-CTxZT3Je.js → p-D7StBSjK.js} +1 -1
  32. package/dist/components/{p-Btj50AUe.js → p-DFbj6d3L.js} +1 -1
  33. package/dist/components/{p-Cugxp5lo.js → p-DlXBFtBO.js} +1 -1
  34. package/dist/components/{p-BIHVGo10.js → p-Du_qPqW-.js} +1 -1
  35. package/dist/components/{p-DJY7lCe4.js → p-DzAvlr8W.js} +1 -1
  36. package/dist/components/{p-4MIzxb0h.js → p-FrqOl9KZ.js} +1 -1
  37. package/dist/components/{p-BQQC-6k4.js → p-JMuxVcTW.js} +1 -1
  38. package/dist/components/{p-B76pFg6k.js → p-OjchKJGr.js} +1 -1
  39. package/dist/components/{p-DIc8C633.js → p-YCGkwgiM.js} +1 -1
  40. package/dist/components/{p-DZikCqcw.js → p-ZvP2Ce6J.js} +1 -1
  41. package/dist/components/p-kRH2GOb0.js +1 -0
  42. package/dist/components/{p-s_75vJTu.js → p-mcMbSkii.js} +1 -1
  43. package/dist/components/{p-DU0IkIvI.js → p-vPS2GNz3.js} +1 -1
  44. package/dist/components/sd-action-modal.js +1 -1
  45. package/dist/components/sd-barcode-input.js +1 -1
  46. package/dist/components/sd-button.js +1 -1
  47. package/dist/components/sd-calendar.js +1 -1
  48. package/dist/components/sd-callout.js +1 -1
  49. package/dist/components/sd-checkbox.js +1 -1
  50. package/dist/components/sd-chip-input.js +1 -1
  51. package/dist/components/sd-chip.js +1 -1
  52. package/dist/components/sd-confirm-modal.js +1 -1
  53. package/dist/components/sd-date-picker-trigger.js +1 -1
  54. package/dist/components/sd-date-picker.js +1 -1
  55. package/dist/components/sd-date-range-picker-calendar.js +1 -1
  56. package/dist/components/sd-date-range-picker.js +1 -1
  57. package/dist/components/sd-dropdown-button.js +1 -1
  58. package/dist/components/sd-field.js +1 -1
  59. package/dist/components/sd-file-picker.js +1 -1
  60. package/dist/components/sd-ghost-button.js +1 -1
  61. package/dist/components/sd-guide.js +1 -1
  62. package/dist/components/sd-icon.js +1 -1
  63. package/dist/components/sd-input.js +1 -1
  64. package/dist/components/sd-key-value-table.js +1 -1
  65. package/dist/components/sd-loading-modal.js +1 -1
  66. package/dist/components/sd-modal-container.js +1 -1
  67. package/dist/components/sd-number-input.js +1 -1
  68. package/dist/components/sd-pagination.js +1 -1
  69. package/dist/components/sd-popover.js +1 -1
  70. package/dist/components/sd-popup.js +1 -1
  71. package/dist/components/sd-select-list-item-search.js +1 -1
  72. package/dist/components/sd-select-list-item.js +1 -1
  73. package/dist/components/sd-select-listbox.js +1 -1
  74. package/dist/components/sd-select-trigger.js +1 -1
  75. package/dist/components/sd-select.js +1 -1
  76. package/dist/components/sd-table.js +1 -1
  77. package/dist/components/sd-tabs.js +1 -1
  78. package/dist/components/sd-tag.js +1 -1
  79. package/dist/components/sd-text-link.js +1 -1
  80. package/dist/components/sd-textarea.js +1 -1
  81. package/dist/components/sd-thead.js +1 -1
  82. package/dist/components/sd-toast-container.js +1 -1
  83. package/dist/components/sd-toast.js +1 -1
  84. package/dist/components/sd-tooltip.js +1 -1
  85. package/dist/components/sd-tr.js +1 -1
  86. package/dist/design-system/design-system.esm.js +1 -1
  87. package/dist/design-system/p-14447dd5.entry.js +1 -0
  88. package/dist/design-system/p-4bbc894b.entry.js +1 -0
  89. package/dist/design-system/p-e7d4b78e.entry.js +1 -0
  90. package/dist/esm/design-system.js +1 -1
  91. package/dist/esm/loader.js +1 -1
  92. package/dist/esm/sd-button_2.entry.js +4 -4
  93. package/dist/esm/sd-chip-input.entry.js +41 -17
  94. package/dist/esm/sd-pagination_4.entry.js +86 -7
  95. package/dist/types/components/sd-table/constants.d.ts +1 -0
  96. package/dist/types/components/sd-table/sd-table.stories.helpers.d.ts +10 -0
  97. package/dist/types/components/sd-table/sd-thead/sd-thead.d.ts +8 -0
  98. package/hydrate/index.js +132 -28
  99. package/hydrate/index.mjs +132 -28
  100. package/package.json +1 -1
  101. package/dist/components/p-BVlg0BoC.js +0 -1
  102. package/dist/components/p-BzsHwLh6.js +0 -1
  103. package/dist/design-system/p-0f35cd21.entry.js +0 -1
  104. package/dist/design-system/p-4e44b45b.entry.js +0 -1
  105. package/dist/design-system/p-fe5b8eaa.entry.js +0 -1
@@ -234,7 +234,7 @@ const SdTbody = class {
234
234
  };
235
235
  SdTbody.style = sdTbodyCss();
236
236
 
237
- const sdTheadCss = () => `sd-thead{display:contents}sd-thead *{box-sizing:border-box}.thead{display:table-header-group;height:var(--table-header-height, 36px)}.thead--sticky{position:sticky;top:0;z-index:120}.tr{display:table-row;width:100%}.th{display:table-cell;background:var(--table-header-bg, #F5FAFF);height:var(--table-header-height, 36px);padding:0 var(--table-header-padding-x, 16px);font-family:var(--table-header-font-family, inherit);font-weight:var(--table-header-font-weight, 500);font-size:var(--table-header-font-size, 12px);line-height:var(--table-header-line-height, 20px);text-decoration:var(--table-header-text-decoration, none);vertical-align:middle;border-bottom:var(--table-border-width, 1px) solid var(--table-border-color, #E1E1E1);-webkit-user-select:none;user-select:none;position:relative;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;word-break:keep-all}.th--selected{position:relative;width:var(--table-selectable-width, 48px) !important;max-width:var(--table-selectable-width, 48px) !important;min-width:var(--table-selectable-width, 48px) !important;text-align:center}.th--selected sd-checkbox label{position:relative}.th--selected sd-checkbox label:before{content:"";position:absolute;inset:-6px}.th.sticky-left{position:sticky;background-color:var(--table-header-bg, #F5FAFF);z-index:110 !important;left:var(--sticky-left-offset, 0)}.th.sticky-right{position:sticky;background-color:var(--table-header-bg, #F5FAFF);z-index:110 !important;right:var(--sticky-right-offset, 0)}.th.sticky-left-edge:after{content:"";position:absolute;top:0;left:100%;right:-20px;width:20px;height:100%;z-index:101 !important;box-shadow:inset 12px 0 20px -25px;opacity:0;pointer-events:none;transition:opacity 0.2s}.th.sticky-right-edge:after{content:"";position:absolute;top:0;left:-20px;width:20px;height:100%;z-index:101 !important;box-shadow:inset -12px 0 20px -25px;opacity:0;pointer-events:none;transition:opacity 0.2s}.th.sticky-left-edge.is-scrolled-left{overflow:visible}.th.sticky-left-edge.is-scrolled-left:after{opacity:1}.th.sticky-right-edge.is-scrolled-right{overflow:visible}.th.sticky-right-edge.is-scrolled-right:after{opacity:1}.th__content{display:flex;flex-flow:row nowrap;align-items:center;gap:var(--table-header-gap, 4px)}.th__content--left{justify-content:flex-start}.th__content--center{justify-content:center}.th__content--right{justify-content:flex-end}.th__content--label{white-space:nowrap;text-overflow:ellipsis;word-break:keep-all}.th__bar{position:absolute;top:50%;right:0;transform:translateY(-50%);width:var(--table-border-width, 1px);height:var(--table-header-resizer-height, 16px);z-index:3;background-color:var(--table-header-resizer-color, #CCCCCC)}.th__resizer{position:absolute;top:50%;right:0;transform:translateY(-50%);width:4px;height:var(--table-header-resizer-height, 16px);cursor:col-resize;z-index:3;border-left:var(--table-border-width, 1px) solid var(--table-header-resizer-color, #CCCCCC);border-right:var(--table-border-width, 1px) solid var(--table-header-resizer-color, #CCCCCC)}.th__resizer--left{right:auto;left:0}`;
237
+ const sdTheadCss = () => `sd-thead{display:contents}sd-thead *{box-sizing:border-box}.thead{display:table-header-group;height:var(--table-header-height, 36px)}.thead--sticky{position:sticky;top:0;z-index:120}.tr{display:table-row;width:100%}.th{display:table-cell;background:var(--table-header-bg, #F5FAFF);height:var(--table-header-height, 36px);padding:0 var(--table-header-padding-x, 16px);font-family:var(--table-header-font-family, inherit);font-weight:var(--table-header-font-weight, 500);font-size:var(--table-header-font-size, 12px);line-height:var(--table-header-line-height, 20px);text-decoration:var(--table-header-text-decoration, none);vertical-align:middle;border-bottom:var(--table-border-width, 1px) solid var(--table-border-color, #E1E1E1);-webkit-user-select:none;user-select:none;position:relative;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;word-break:keep-all}.th--selected{position:relative;width:var(--table-selectable-width, 48px) !important;max-width:var(--table-selectable-width, 48px) !important;min-width:var(--table-selectable-width, 48px) !important;text-align:center}.th--selected sd-checkbox label{position:relative}.th--selected sd-checkbox label:before{content:"";position:absolute;inset:-6px}.th.sticky-left{position:sticky;background-color:var(--table-header-bg, #F5FAFF);z-index:110 !important;left:var(--sticky-left-offset, 0)}.th.sticky-right{position:sticky;background-color:var(--table-header-bg, #F5FAFF);z-index:110 !important;right:var(--sticky-right-offset, 0)}.th.sticky-left-edge:after{content:"";position:absolute;top:0;left:100%;right:-20px;width:20px;height:100%;z-index:101 !important;box-shadow:inset 12px 0 20px -25px;opacity:0;pointer-events:none;transition:opacity 0.2s}.th.sticky-right-edge:after{content:"";position:absolute;top:0;left:-20px;width:20px;height:100%;z-index:101 !important;box-shadow:inset -12px 0 20px -25px;opacity:0;pointer-events:none;transition:opacity 0.2s}.th.sticky-left-edge.is-scrolled-left{overflow:visible}.th.sticky-left-edge.is-scrolled-left:after{opacity:1}.th.sticky-right-edge.is-scrolled-right{overflow:visible}.th.sticky-right-edge.is-scrolled-right:after{opacity:1}.th__content{display:flex;flex-flow:row nowrap;align-items:center;gap:var(--table-header-gap, 4px)}.th__content--left{justify-content:flex-start}.th__content--center{justify-content:center}.th__content--right{justify-content:flex-end}.th__content--label{white-space:nowrap;text-overflow:ellipsis;word-break:keep-all}.th__bar{position:absolute;top:50%;right:0;transform:translateY(-50%);width:var(--table-border-width, 1px);height:var(--table-header-resizer-height, 16px);z-index:3;background-color:var(--table-header-resizer-color, #CCCCCC)}.th__dropdown-trigger{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:16px;height:16px;padding:0;border:none;background:transparent;cursor:pointer;color:inherit;border-radius:2px}.th__dropdown-trigger sd-icon{transition:transform 0.15s ease}.th__dropdown-trigger--open sd-icon{transform:rotate(180deg)}.th__resizer{position:absolute;top:50%;right:0;transform:translateY(-50%);width:4px;height:var(--table-header-resizer-height, 16px);cursor:col-resize;z-index:3;border-left:var(--table-border-width, 1px) solid var(--table-header-resizer-color, #CCCCCC);border-right:var(--table-border-width, 1px) solid var(--table-header-resizer-color, #CCCCCC)}.th__resizer--left{right:auto;left:0}`;
238
238
 
239
239
  const SdThead = class {
240
240
  constructor(hostRef) {
@@ -270,14 +270,29 @@ const SdThead = class {
270
270
  _stickyHeader = false;
271
271
  _scrolledLeft = false;
272
272
  _scrolledRight = false;
273
+ dropdownOpenMap = {};
273
274
  tableEl = null;
275
+ dropdownRefs = {};
276
+ dropdownThRefs = {};
277
+ dropdownPortals = {};
274
278
  handleColumnsChange(newCols) {
275
279
  if (Array.isArray(newCols) && (this.columnWidths ?? []).length === 0) {
276
280
  this.columnWidths = newCols.map(c => (c.autoWidth ? 0 : parseInt(c.width || '120', 10)));
277
281
  }
278
282
  }
283
+ disconnectedCallback() {
284
+ const portals = this.dropdownPortals ?? {};
285
+ Object.values(portals).forEach(portal => {
286
+ if (portal?.parentNode != null)
287
+ portal.parentNode.removeChild(portal);
288
+ });
289
+ }
279
290
  componentWillLoad() {
280
291
  this.syncTableContext();
292
+ this.dropdownOpenMap = this.dropdownOpenMap ?? {};
293
+ this.dropdownRefs = this.dropdownRefs ?? {};
294
+ this.dropdownThRefs = this.dropdownThRefs ?? {};
295
+ this.dropdownPortals = this.dropdownPortals ?? {};
281
296
  this.columnWidths = Array.isArray(this.columnWidths) ? this.columnWidths : [];
282
297
  this.resolveConfig();
283
298
  if ((this.columnWidths ?? []).length === 0) {
@@ -287,6 +302,36 @@ const SdThead = class {
287
302
  componentDidLoad() {
288
303
  this.syncTableContext();
289
304
  this.resolveConfig();
305
+ this.initDropdownPortals();
306
+ }
307
+ // sd-portal을 body에 붙여 CSS transform 영향 없이 정확한 위치에 패널을 표시한다.
308
+ // <slot>을 render에 두지 않고 직접 DOM 이동(slot 속성 제거)으로 Stencil 슬롯 에뮬레이션 충돌을 피한다.
309
+ initDropdownPortals() {
310
+ const cols = this._columns ?? [];
311
+ const hasDropdownCols = cols.filter(c => c.hasDropdown === true);
312
+ if (hasDropdownCols.length === 0)
313
+ return;
314
+ for (const col of hasDropdownCols) {
315
+ const colName = col.name;
316
+ // 이미 포탈이 생성된 컬럼은 건너뜀 (refreshConfig 재호출 대비)
317
+ if ((this.dropdownPortals ?? {})[colName] != null)
318
+ continue;
319
+ const slotContent = this.el.querySelector(`[slot="dropdown-${colName}"]`);
320
+ if (slotContent == null)
321
+ continue;
322
+ // slot 속성 제거: Stencil 슬롯 에뮬레이션이 재배포하지 못하게 한다
323
+ slotContent.removeAttribute('slot');
324
+ const portal = document.createElement('sd-portal');
325
+ portal.parentRef = this.dropdownThRefs[colName] ?? null;
326
+ portal.open = false;
327
+ // 첫 open 전까지 포탈 호스트 자체를 숨긴다.
328
+ // moveSlotContent()가 open=true 시 children을 독립 wrapper div로 이동하므로 이후 동작에 영향 없음.
329
+ portal.style.display = 'none';
330
+ portal.addEventListener('sdClose', () => this.closeDropdown(colName));
331
+ portal.appendChild(slotContent);
332
+ document.body.appendChild(portal);
333
+ this.dropdownPortals[colName] = portal;
334
+ }
290
335
  }
291
336
  syncTableContext() {
292
337
  // sd-table이 shadow:true이므로 fallback content로 렌더되면 closest가 경계를 못 넘는다.
@@ -329,6 +374,7 @@ const SdThead = class {
329
374
  /** 테이블 설정을 다시 불러옵니다. */
330
375
  async refreshConfig() {
331
376
  this.resolveConfig();
377
+ this.initDropdownPortals();
332
378
  }
333
379
  /** 컬럼 너비를 업데이트합니다. */
334
380
  async setColumnWidths(widths) {
@@ -373,6 +419,28 @@ const SdThead = class {
373
419
  handleColumnSort = (col) => {
374
420
  this.sdColumnSort.emit({ column: col });
375
421
  };
422
+ toggleDropdown(name) {
423
+ const isOpen = this.dropdownOpenMap[name] !== true;
424
+ const portals = this.dropdownPortals ?? {};
425
+ const newMap = { ...this.dropdownOpenMap };
426
+ // _columns 기준으로 순회: portals가 비어도 dropdownOpenMap은 항상 갱신됨
427
+ for (const col of (this._columns ?? []).filter(c => c.hasDropdown === true)) {
428
+ const shouldBeOpen = col.name === name ? isOpen : false;
429
+ newMap[col.name] = shouldBeOpen;
430
+ const portal = portals[col.name];
431
+ if (portal != null)
432
+ portal.open = shouldBeOpen;
433
+ }
434
+ this.dropdownOpenMap = newMap;
435
+ }
436
+ closeDropdown(name) {
437
+ if (this.dropdownOpenMap[name] !== true)
438
+ return;
439
+ this.dropdownOpenMap = { ...this.dropdownOpenMap, [name]: false };
440
+ const portal = (this.dropdownPortals ?? {})[name];
441
+ if (portal != null)
442
+ portal.open = false;
443
+ }
376
444
  handleSelectAll(checked) {
377
445
  const nextChecked = !!checked;
378
446
  const safeRows = Array.isArray(this.rows) ? this.rows : [];
@@ -389,6 +457,8 @@ const SdThead = class {
389
457
  }
390
458
  render() {
391
459
  const safeRows = Array.isArray(this.rows) ? this.rows : [];
460
+ const dropdownOpenMap = this.dropdownOpenMap ?? {};
461
+ const dropdownRefs = this.dropdownRefs ?? {};
392
462
  const stickyLeftCount = this._stickyColumn.left || 0;
393
463
  const stickyRightCount = this._stickyColumn.right || 0;
394
464
  const stickyLeftCols = this.visibleColumns.slice(0, stickyLeftCount);
@@ -410,22 +480,25 @@ const SdThead = class {
410
480
  '--table-border-color': sdTable_config.TABLE_BORDER.color,
411
481
  '--table-border-width': `${sdTable_config.TABLE_BORDER.width}px`,
412
482
  };
413
- return (index.h(index.Host, { key: '26981191de95775401cad61e80fbf1e22cb0250b', slot: `${this.tableId}-head`, style: headStyle }, index.h("thead", { key: 'a53d430f1b71a45b98de2912b563d3f17cd20b24', class: {
483
+ return (index.h(index.Host, { key: '947f5e5b0e59e7e6f8a73e4fd44dbfa646cb630a', slot: `${this.tableId}-head`, style: headStyle }, index.h("thead", { key: '2e0ffbfe9d96528c6e0082d4883fa462a18831d6', class: {
414
484
  'thead': true,
415
485
  'thead--sticky': this._stickyHeader,
416
- } }, index.h("tr", { key: '27ce13b70bfda4b210b36eb746fb60d369d9f5be', class: "tr" }, this._selectable && (index.h("th", { key: 'e353b00c7a02b82781e5107b1fd73dc556427edf', class: {
486
+ } }, index.h("tr", { key: '5843a18e1548bc6546e447d4c9213375116af0e9', class: "tr" }, this._selectable && (index.h("th", { key: '914024ca35874747e39f1819337cd4ad1cbe733d', class: {
417
487
  'th': true,
418
488
  'th--selected': true,
419
489
  'sticky-left': true,
420
490
  'sticky-left-edge': stickyLeftCount === 0,
421
491
  'is-scrolled-left': stickyLeftCount === 0 && this._scrolledLeft,
422
- }, style: { '--sticky-left-offset': '0px' } }, index.h("sd-checkbox", { key: '18eeabbcc4fc10196d6448bd9d0493b55778f9f1', value: this.getIsAllChecked(), disabled: safeRows.length === 0, onSdUpdate: (e) => this.handleSelectAll(e.detail) }), index.h("div", { key: '1a5a0561fc94be53a8244827ac9ae4f98fa766c2', class: "th__bar" }))), stickyLeftCols.map((col, idx) => (index.h("th", { key: col.name, class: {
492
+ }, style: { '--sticky-left-offset': '0px' } }, index.h("sd-checkbox", { key: 'e07232f1f5fa02589573ddaae53ebdadbdb97674', value: this.getIsAllChecked(), disabled: safeRows.length === 0, onSdUpdate: (e) => this.handleSelectAll(e.detail) }), index.h("div", { key: '08b504cfd4f420e76e91d649eed28c7068517044', class: "th__bar" }))), stickyLeftCols.map((col, idx) => (index.h("th", { key: col.name, class: {
423
493
  'th': true,
424
494
  [`${col.thClass}`]: Boolean(col.thClass),
425
495
  'sticky-left': true,
426
496
  'sticky-left-edge': idx === stickyLeftCount - 1,
427
497
  'is-scrolled-left': idx === stickyLeftCount - 1 && this._scrolledLeft,
428
- }, style: { ...col.thStyle, ...this.getStickyStyle(idx) } }, index.h("div", { class: `th__content th__content--${col.align || 'left'}` }, index.h("div", { class: "th__content--label" }, col.label), col.icon && (index.h("sd-icon", { name: col.icon, size: "12", color: sdTable_config.resolveTableIconColor(col.icon, col.iconColor) })), col.sort && (index.h("sd-icon", { name: sdTable_config.resolveSortIconName(col.sort), size: "12", color: sdTable_config.resolveTableIconColor(sdTable_config.resolveSortIconName(col.sort), col.iconColor), onClick: () => this.handleColumnSort(col), style: { cursor: 'pointer' } })), col.tooltip && (index.h("sd-tooltip", { ...col.tooltipOptions }, index.h("div", { slot: "content" }, col.tooltip.map(text => (index.h("p", null, text))))))), this._resizable && idx !== lastColIdx && (
498
+ }, style: { ...col.thStyle, ...this.getStickyStyle(idx) }, ref: col.hasDropdown === true ? (el => { this.dropdownThRefs[col.name] = el; }) : undefined }, index.h("div", { class: `th__content th__content--${col.align || 'left'}` }, index.h("div", { class: "th__content--label" }, col.label), col.icon && (index.h("sd-icon", { name: col.icon, size: "12", color: sdTable_config.resolveTableIconColor(col.icon, col.iconColor) })), col.sort && (index.h("sd-icon", { name: sdTable_config.resolveSortIconName(col.sort), size: "12", color: sdTable_config.resolveTableIconColor(sdTable_config.resolveSortIconName(col.sort), col.iconColor), onClick: () => this.handleColumnSort(col), style: { cursor: 'pointer' } })), col.tooltip && (index.h("sd-tooltip", { ...col.tooltipOptions }, index.h("div", { slot: "content" }, col.tooltip.map(text => (index.h("p", null, text)))))), col.hasDropdown === true && (index.h("button", { class: {
499
+ 'th__dropdown-trigger': true,
500
+ 'th__dropdown-trigger--open': dropdownOpenMap[col.name] === true,
501
+ }, ref: el => { dropdownRefs[col.name] = el; }, onClick: () => this.toggleDropdown(col.name), "aria-haspopup": "listbox", "aria-expanded": dropdownOpenMap[col.name] === true ? 'true' : 'false' }, index.h("sd-icon", { name: "chevronDown", size: "12" })))), this._resizable && idx !== lastColIdx && (
429
502
  // column resize 는 마우스 드래그 전용 인터랙션이라 키보드 핸들러를 두지 않는다.
430
503
  // eslint-disable-next-line jsx-a11y/no-static-element-interactions
431
504
  index.h("div", { class: "th__resizer", onMouseDown: (evt) => this.handleResize(idx, evt) }))))), middleCols.map((col, relativeIdx) => {
@@ -433,7 +506,10 @@ const SdThead = class {
433
506
  return (index.h("th", { key: col.name, class: {
434
507
  th: true,
435
508
  [`${col.thClass}`]: Boolean(col.thClass),
436
- }, style: { ...col.thStyle, ...this.getStickyStyle(actualColIdx) } }, index.h("div", { class: `th__content th__content--${col.align || 'left'}` }, index.h("div", { class: "th__content--label" }, col.label), col.icon && (index.h("sd-icon", { name: col.icon, size: "12", color: sdTable_config.resolveTableIconColor(col.icon, col.iconColor) })), col.sort && (index.h("sd-icon", { name: sdTable_config.resolveSortIconName(col.sort), size: "12", color: sdTable_config.resolveTableIconColor(sdTable_config.resolveSortIconName(col.sort), col.iconColor), onClick: () => this.handleColumnSort(col), style: { cursor: 'pointer' } })), col.tooltip && (index.h("sd-tooltip", { ...col.tooltipOptions }, index.h("div", { slot: "content" }, col.tooltip.map(text => (index.h("p", null, text))))))), this._resizable && actualColIdx !== lastColIdx && (
509
+ }, style: { ...col.thStyle, ...this.getStickyStyle(actualColIdx) }, ref: col.hasDropdown === true ? (el => { this.dropdownThRefs[col.name] = el; }) : undefined }, index.h("div", { class: `th__content th__content--${col.align || 'left'}` }, index.h("div", { class: "th__content--label" }, col.label), col.icon && (index.h("sd-icon", { name: col.icon, size: "12", color: sdTable_config.resolveTableIconColor(col.icon, col.iconColor) })), col.sort && (index.h("sd-icon", { name: sdTable_config.resolveSortIconName(col.sort), size: "12", color: sdTable_config.resolveTableIconColor(sdTable_config.resolveSortIconName(col.sort), col.iconColor), onClick: () => this.handleColumnSort(col), style: { cursor: 'pointer' } })), col.tooltip && (index.h("sd-tooltip", { ...col.tooltipOptions }, index.h("div", { slot: "content" }, col.tooltip.map(text => (index.h("p", null, text)))))), col.hasDropdown === true && (index.h("button", { class: {
510
+ 'th__dropdown-trigger': true,
511
+ 'th__dropdown-trigger--open': this.dropdownOpenMap[col.name] === true,
512
+ }, ref: el => { this.dropdownRefs[col.name] = el; }, onClick: () => this.toggleDropdown(col.name), "aria-haspopup": "listbox", "aria-expanded": this.dropdownOpenMap[col.name] === true ? 'true' : 'false' }, index.h("sd-icon", { name: "chevronDown", size: "12" })))), this._resizable && actualColIdx !== lastColIdx && (
437
513
  // column resize 는 마우스 드래그 전용 인터랙션이라 키보드 핸들러를 두지 않는다.
438
514
  // eslint-disable-next-line jsx-a11y/no-static-element-interactions
439
515
  index.h("div", { class: "th__resizer", onMouseDown: (evt) => this.handleResize(actualColIdx, evt) }))));
@@ -445,7 +521,10 @@ const SdThead = class {
445
521
  'sticky-right': true,
446
522
  'sticky-right-edge': relativeIdx === 0,
447
523
  'is-scrolled-right': relativeIdx === 0 && this._scrolledRight,
448
- }, style: { ...col.thStyle, ...this.getStickyStyle(actualColIdx) } }, index.h("div", { class: `th__content th__content--${col.align || 'left'}` }, index.h("div", { class: "th__content--label" }, col.label), col.icon && (index.h("sd-icon", { name: col.icon, size: "12", color: sdTable_config.resolveTableIconColor(col.icon, col.iconColor) })), col.sort && (index.h("sd-icon", { name: sdTable_config.resolveSortIconName(col.sort), size: "12", color: sdTable_config.resolveTableIconColor(sdTable_config.resolveSortIconName(col.sort), col.iconColor), onClick: () => this.handleColumnSort(col), style: { cursor: 'pointer' } })), col.tooltip && (index.h("sd-tooltip", { ...col.tooltipOptions }, index.h("div", { slot: "content" }, col.tooltip.map(text => (index.h("p", null, text))))))), this._resizable && (
524
+ }, style: { ...col.thStyle, ...this.getStickyStyle(actualColIdx) }, ref: col.hasDropdown === true ? (el => { this.dropdownThRefs[col.name] = el; }) : undefined }, index.h("div", { class: `th__content th__content--${col.align || 'left'}` }, index.h("div", { class: "th__content--label" }, col.label), col.icon && (index.h("sd-icon", { name: col.icon, size: "12", color: sdTable_config.resolveTableIconColor(col.icon, col.iconColor) })), col.sort && (index.h("sd-icon", { name: sdTable_config.resolveSortIconName(col.sort), size: "12", color: sdTable_config.resolveTableIconColor(sdTable_config.resolveSortIconName(col.sort), col.iconColor), onClick: () => this.handleColumnSort(col), style: { cursor: 'pointer' } })), col.tooltip && (index.h("sd-tooltip", { ...col.tooltipOptions }, index.h("div", { slot: "content" }, col.tooltip.map(text => (index.h("p", null, text)))))), col.hasDropdown === true && (index.h("button", { class: {
525
+ 'th__dropdown-trigger': true,
526
+ 'th__dropdown-trigger--open': this.dropdownOpenMap[col.name] === true,
527
+ }, ref: el => { this.dropdownRefs[col.name] = el; }, onClick: () => this.toggleDropdown(col.name), "aria-haspopup": "listbox", "aria-expanded": this.dropdownOpenMap[col.name] === true ? 'true' : 'false' }, index.h("sd-icon", { name: "chevronDown", size: "12" })))), this._resizable && (
449
528
  // column resize 는 마우스 드래그 전용 인터랙션이라 키보드 핸들러를 두지 않는다.
450
529
  // eslint-disable-next-line jsx-a11y/no-static-element-interactions
451
530
  index.h("div", { class: "th__resizer th__resizer--left", onMouseDown: (evt) => this.handleResize(actualColIdx, evt, true) }))));
@@ -1,5 +1,5 @@
1
1
  import { h } from "@stencil/core";
2
2
  export const CaretDown = (props) => {
3
3
  const { size = 24, ...rest } = props;
4
- return (h("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...rest }, h("path", { d: "M11.8033 17.754L2.72757 6.40942C2.59557 6.24442 2.71304 6 2.92435 6H21.0757C21.287 6 21.4045 6.24442 21.2725 6.40942L12.1968 17.754C12.0959 17.8801 11.9041 17.8801 11.8033 17.754Z", fill: "currentColor", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" })));
4
+ return (h("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...rest }, h("path", { d: "M19.2188 8.25C19.9555 8.25 20.2813 9.44246 19.7061 10.0342L12.4874 17.46C12.2028 17.7526 11.7974 17.7526 11.5127 17.46L4.294 10.0342C3.71879 9.44247 4.04461 8.25 4.7813 8.25H19.2188Z", fill: "currentColor" })));
5
5
  };
@@ -1,5 +1,5 @@
1
1
  import { h } from "@stencil/core";
2
2
  export const CaretLeft = (props) => {
3
3
  const { size = 24, ...rest } = props;
4
- return (h("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...rest }, h("path", { d: "M6.24594 12.1968L17.5905 21.2725C17.7555 21.4045 18 21.287 18 21.0757V2.92435C18 2.71304 17.7555 2.59557 17.5905 2.72757L6.24594 11.8033C6.11984 11.9041 6.11984 12.0959 6.24594 12.1968Z", fill: "currentColor", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" })));
4
+ return (h("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...rest }, h("path", { d: "M14.1423 4.29416C14.7698 3.71888 16.0358 4.04475 16.0358 4.78146V19.219C16.0358 19.9557 14.7698 20.2815 14.1423 19.7063L6.26827 12.4865C5.95813 12.2019 5.95791 11.7975 6.26827 11.5129L14.1423 4.29416Z", fill: "currentColor" })));
5
5
  };
@@ -1,5 +1,5 @@
1
1
  import { h } from "@stencil/core";
2
2
  export const CaretRight = (props) => {
3
3
  const { size = 24, ...rest } = props;
4
- return (h("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...rest }, h("path", { d: "M17.754 12.1968L6.40942 21.2725C6.24442 21.4045 6 21.287 6 21.0757L6 2.92435C6 2.71304 6.24442 2.59557 6.40942 2.72757L17.754 11.8033C17.8801 11.9041 17.8801 12.0959 17.754 12.1968Z", fill: "currentColor", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" })));
4
+ return (h("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...rest }, h("path", { d: "M8.25 4.78127C8.25 4.04456 9.44248 3.71871 10.0342 4.29397L17.46 11.5127C17.7526 11.7973 17.7526 12.2027 17.46 12.4873L10.0342 19.7061C9.44249 20.2814 8.25 19.9555 8.25 19.2188V4.78127Z", fill: "currentColor" })));
5
5
  };
@@ -1,5 +1,5 @@
1
1
  import { h } from "@stencil/core";
2
2
  export const CaretUp = (props) => {
3
3
  const { size = 24, ...rest } = props;
4
- return (h("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...rest }, h("path", { d: "M11.8033 6.24594L2.72757 17.5905C2.59557 17.7555 2.71304 18 2.92435 18H21.0757C21.287 18 21.4045 17.7555 21.2725 17.5905L12.1968 6.24594C12.0959 6.11984 11.9041 6.11984 11.8033 6.24594Z", fill: "currentColor", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" })));
4
+ return (h("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...rest }, h("path", { d: "M11.6252 6.44362C11.8583 6.27919 12.1422 6.27906 12.3752 6.44362L12.4865 6.5403L19.7063 13.9651C20.2815 14.5568 19.9557 15.7503 19.219 15.7503H4.78146C4.04475 15.7503 3.71888 14.5568 4.29416 13.9651L11.5129 6.5403L11.6252 6.44362Z", fill: "currentColor" })));
5
5
  };
@@ -133,6 +133,11 @@ sd-chip-input .sd-chip-input__suggestion-item--active {
133
133
  color: var(--sd-chip-input-suggestion-color-hover);
134
134
  font-weight: var(--sd-chip-input-suggestion-font-weight-selected);
135
135
  }
136
+ sd-chip-input .sd-chip-input__suggestion-item--selected {
137
+ cursor: default;
138
+ overflow: visible;
139
+ text-overflow: unset;
140
+ }
136
141
  sd-chip-input .sd-chip-input__suggestions-empty {
137
142
  padding: 12px;
138
143
  text-align: center;
@@ -222,7 +222,9 @@ export class SdChipInput {
222
222
  }
223
223
  if (event.key === 'Enter' && this.activeSuggestionIndex >= 0) {
224
224
  event.preventDefault();
225
- this.addChip(activeItems[this.activeSuggestionIndex]);
225
+ const selectedItem = activeItems[this.activeSuggestionIndex];
226
+ if (!(this.internalValues ?? []).includes(selectedItem))
227
+ this.addChip(selectedItem);
226
228
  return;
227
229
  }
228
230
  }
@@ -282,6 +284,10 @@ export class SdChipInput {
282
284
  };
283
285
  handleContainerClick = () => {
284
286
  if (!this.disabled) {
287
+ // 이미 포커스된 상태에서 재클릭 시 handleFocus가 다시 실행되지 않으므로
288
+ // suppress 플래그를 여기서 직접 해제한다.
289
+ this.suppressRecommendations = false;
290
+ this.suppressSuggestions = false;
285
291
  this.inputEl?.focus();
286
292
  }
287
293
  };
@@ -426,7 +432,7 @@ export class SdChipInput {
426
432
  fontFamily: CHIP_INPUT_SUGGESTIONS.itemFontFamily,
427
433
  color: CHIP_INPUT_SUGGESTIONS.emptyColor,
428
434
  };
429
- return (h("sd-field", { key: '7135f3f1fe7c76276bcd780ba2cfdf391925a57b', name: this.name, label: this.label, labelWidth: this.labelWidth, hint: this.hint, errorMessage: this.errorMessage, rules: [this.duplicateRule, ...(this.rules ?? [])], error: this.error, disabled: this.disabled, focused: this.focused, hovered: this.hovered, status: this.status, size: this.size, width: this.width, icon: this.icon, labelTooltip: this.labelTooltip, labelTooltipProps: this.labelTooltipProps, ref: el => (this.formField = el), style: cssVars, onMouseEnter: () => (this.hovered = true), onMouseLeave: () => (this.hovered = false) }, h("div", { key: '8566ae7bc3bedaf6be95f29267587c49c090e73f', class: "sd-chip-input", ref: el => (this.chipInputRef = el), onClick: this.handleContainerClick }, (this.internalValues ?? []).map((value, i) => (h("sd-chip", { key: i, value: value, error: this.getChipError(i), disabled: this.getChipDisabled(i), editable: false, onSdRemove: () => this.handleChipRemove(i) }))), !isMaxReached && (h("div", { key: '00d9946c93d2e961466fa8baf617fc4ddc12d701', class: {
435
+ return (h("sd-field", { key: 'b72e33b6f204ebdf6791cc2f20d01bbb74a4203a', name: this.name, label: this.label, labelWidth: this.labelWidth, hint: this.hint, errorMessage: this.errorMessage, rules: [this.duplicateRule, ...(this.rules ?? [])], error: this.error, disabled: this.disabled, focused: this.focused, hovered: this.hovered, status: this.status, size: this.size, width: this.width, icon: this.icon, labelTooltip: this.labelTooltip, labelTooltipProps: this.labelTooltipProps, ref: el => (this.formField = el), style: cssVars, onMouseEnter: () => (this.hovered = true), onMouseLeave: () => (this.hovered = false) }, h("div", { key: '499114d06f4d375fed5575edd394c501f4d72691', class: "sd-chip-input", ref: el => (this.chipInputRef = el), onClick: this.handleContainerClick }, (this.internalValues ?? []).map((value, i) => (h("sd-chip", { key: i, value: value, error: this.getChipError(i), disabled: this.getChipDisabled(i), editable: false, onSdRemove: () => this.handleChipRemove(i) }))), !isMaxReached && (h("div", { key: '5910a2d0e0e9cbc472baa1d9c9c3fa07e89fbdcb', class: {
430
436
  'sd-chip-input__field': true,
431
437
  'sd-chip-input__field--filled': this.inputValue !== '',
432
438
  'sd-chip-input__field--empty': this.inputValue === '' && this.internalValues.length > 0 && !this.focused,
@@ -434,20 +440,38 @@ export class SdChipInput {
434
440
  ? this.inputValue
435
441
  : this.internalValues.length === 0
436
442
  ? this.placeholder
437
- : '' }, h("input", { key: '2845394a5eaec2d5f37876a0e8938fa9d3f38263', ref: el => (this.inputEl = el), class: "sd-chip-input__input", placeholder: this.internalValues.length === 0 ? this.placeholder : '', disabled: this.disabled, size: 1, onInput: this.handleInput, onKeyDown: this.handleKeyDown, onFocus: this.handleFocus, onBlur: this.handleBlur }))), showResetArea && (h("div", { key: '7cec3884a4cace3d688e80006d6240d321ac60f3', class: "sd-chip-input__reset" }, this.inputValue === '' && h("span", { key: '761539e242437841a2703d95903f9d6b5fe3d69f', class: "sd-chip-input__divider" }), this.maxCount != null && (h("span", { key: '1b448973ba3573c94ae09b74cd24720c8ea011d3', class: "sd-chip-input__max-count" }, "\uCD5C\uB300 ", this.maxCount, "\uAC1C")), this.useReset && this.internalValues.length > 0 && (h("sd-text-link", { key: '90d7582f35b8e7a8ee3c1dba717fa87c13c1de19', icon: "reset", iconColor: CHIP_INPUT_RESET.iconColor, label: "\uC785\uB825 \uCD08\uAE30\uD654", underline: true, disabled: this.disabled, style: { '--sd-text-link-color': CHIP_INPUT_RESET.labelColor }, onSdClick: this.handleResetInput })))), showDropdown && (h("sd-portal", { key: '9495c87a4916829fe955f054a8c97d2b3fa04914', open: showDropdown, parentRef: this.chipInputRef ?? null, viewportPadding: SdChipInput.SUGGESTIONS_VIEWPORT_PADDING, onSdClose: this.closeSuggestions }, h("div", { key: 'ac4afd8511316f0b4b957c312a051b02fc72f20e', class: "sd-chip-input__suggestions", role: "listbox", style: suggestionBoxStyle }, isDropdownLoading && (h("div", { key: '3b6855a7ad2ef48415160544a19014d59ca38ec1', class: "sd-chip-input__suggestions-empty", style: suggestionEmptyStyle }, "\uBD88\uB7EC\uC624\uB294 \uC911...")), dropdownItems.map((item, i) => (h("div", { key: item, class: {
438
- 'sd-chip-input__suggestion-item': true,
439
- 'sd-chip-input__suggestion-item--active': i === this.activeSuggestionIndex,
440
- }, role: "option", tabIndex: -1, "aria-selected": i === this.activeSuggestionIndex, style: i === this.activeSuggestionIndex
441
- ? {
442
- ...suggestionItemStyle,
443
- background: CHIP_INPUT_SUGGESTIONS.itemBgHover,
444
- color: CHIP_INPUT_SUGGESTIONS.itemColorHover,
445
- fontWeight: CHIP_INPUT_SUGGESTIONS.itemFontWeightSelected,
446
- }
447
- : suggestionItemStyle, onMouseDown: e => {
448
- e.preventDefault();
449
- this.addChip(item);
450
- } }, item)))))))));
443
+ : '' }, h("input", { key: '3c1e75d691332b67a500a2d17030d5db191bdeb9', ref: el => (this.inputEl = el), class: "sd-chip-input__input", placeholder: this.internalValues.length === 0 ? this.placeholder : '', disabled: this.disabled, size: 1, onInput: this.handleInput, onKeyDown: this.handleKeyDown, onFocus: this.handleFocus, onBlur: this.handleBlur }))), showResetArea && (
444
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
445
+ h("div", { key: '4aa212280a928b88b85dffa3d453ea8f563e1fe0', class: "sd-chip-input__reset",
446
+ // 리셋 버튼 클릭 input blur portal unmount refocus → 새 portal mount로
447
+ // 두 wrapper가 body에 잠깐 겹치는 타이밍 이슈를 방지한다.
448
+ onMouseDown: e => e.preventDefault() }, this.inputValue === '' && h("span", { key: 'f7faa45b57d8e38dda8cc98119357f92d914fee4', class: "sd-chip-input__divider" }), this.maxCount != null && (h("span", { key: 'dfde4ea82558414614fb2d817f7bfa1fb5d4bf5f', class: "sd-chip-input__max-count" }, "\uCD5C\uB300 ", this.maxCount, "\uAC1C")), this.useReset && this.internalValues.length > 0 && (h("sd-text-link", { key: '9b43c82c1a5d425e5b187e92b4259e7d919491e3', icon: "reset", iconColor: CHIP_INPUT_RESET.iconColor, label: "\uC785\uB825 \uCD08\uAE30\uD654", underline: true, disabled: this.disabled, style: { '--sd-text-link-color': CHIP_INPUT_RESET.labelColor }, onSdClick: this.handleResetInput })))), showDropdown && (h("sd-portal", { key: 'aa205aacbbafcd8c3d9855268637e6da956d4a74', open: showDropdown, parentRef: this.chipInputRef ?? null, viewportPadding: SdChipInput.SUGGESTIONS_VIEWPORT_PADDING, onSdClose: this.closeSuggestions }, h("div", { key: '26814734f202da3a13e695ae1d0c913ea430b888', class: "sd-chip-input__suggestions", role: "listbox", style: suggestionBoxStyle }, isDropdownLoading && (h("div", { key: 'df80d11b79f67dc6cfe6503b258528efa3752265', class: "sd-chip-input__suggestions-empty", style: suggestionEmptyStyle }, "\uBD88\uB7EC\uC624\uB294 \uC911...")), dropdownItems.map((item, i) => {
449
+ const isSelected = (this.internalValues ?? []).includes(item);
450
+ const isActive = i === this.activeSuggestionIndex && !isSelected;
451
+ return (h("div", { key: item, class: {
452
+ 'sd-chip-input__suggestion-item': true,
453
+ 'sd-chip-input__suggestion-item--active': isActive,
454
+ 'sd-chip-input__suggestion-item--selected': isSelected,
455
+ }, role: "option", tabIndex: -1, "aria-selected": isSelected || isActive, "aria-disabled": isSelected ? 'true' : undefined, style: isSelected
456
+ ? {
457
+ ...suggestionItemStyle,
458
+ color: '#0075FF',
459
+ fontWeight: CHIP_INPUT_SUGGESTIONS.itemFontWeightSelected,
460
+ cursor: 'default',
461
+ }
462
+ : isActive
463
+ ? {
464
+ ...suggestionItemStyle,
465
+ background: CHIP_INPUT_SUGGESTIONS.itemBgHover,
466
+ color: CHIP_INPUT_SUGGESTIONS.itemColorHover,
467
+ fontWeight: CHIP_INPUT_SUGGESTIONS.itemFontWeightSelected,
468
+ }
469
+ : suggestionItemStyle, onMouseDown: e => {
470
+ e.preventDefault();
471
+ if (!isSelected)
472
+ this.addChip(item);
473
+ } }, h("span", { style: { flex: '1' } }, item), isSelected && (h("sd-icon", { name: "check", size: "12", style: { color: '#0075FF', flexShrink: '0' } }))));
474
+ })))))));
451
475
  }
452
476
  static get is() { return "sd-chip-input"; }
453
477
  static get originalStyleUrls() {
@@ -143,3 +143,100 @@ StoryVirtualTable = __decorate([
143
143
  customElement('story-virtual-table')
144
144
  ], StoryVirtualTable);
145
145
  export { StoryVirtualTable };
146
+ const DROPDOWN_STATUS_META = {
147
+ active: { bg: '#DCFCE7', color: '#166534', label: 'Active' },
148
+ reviewing: { bg: '#FEF3C7', color: '#92400E', label: 'Reviewing' },
149
+ archived: { bg: '#E2E8F0', color: '#475569', label: 'Archived' },
150
+ };
151
+ const DROPDOWN_SEED = [
152
+ { id: 1, name: 'Avery Kim', role: 'Owner', region: 'Seoul', status: 'active' },
153
+ { id: 2, name: 'Mina Park', role: 'Manager', region: 'Busan', status: 'reviewing' },
154
+ { id: 3, name: 'Ethan Lee', role: 'Analyst', region: 'Incheon', status: 'archived' },
155
+ { id: 4, name: 'Sora Choi', role: 'Owner', region: 'Daegu', status: 'active' },
156
+ { id: 5, name: 'Jisoo Han', role: 'Manager', region: 'Daejeon', status: 'reviewing' },
157
+ { id: 6, name: 'Noah Yoon', role: 'Analyst', region: 'Gwangju', status: 'active' },
158
+ { id: 7, name: 'Iris Jung', role: 'Manager', region: 'Suwon', status: 'active' },
159
+ { id: 8, name: 'Liam Seo', role: 'Owner', region: 'Ulsan', status: 'reviewing' },
160
+ ];
161
+ const DROPDOWN_COLUMNS = [
162
+ { name: 'name', label: 'Customer', field: 'name', autoWidth: true },
163
+ { name: 'role', label: 'Role', field: 'role', align: 'center', width: '100px', hasDropdown: true },
164
+ { name: 'region', label: 'Region', field: 'region', width: '100px' },
165
+ { name: 'status', label: 'Status', field: 'status', align: 'center', width: '110px', hasDropdown: true },
166
+ ];
167
+ const LISTBOX_STYLE = 'margin:0;padding:4px;list-style:none;min-width:140px;background:white;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,0.10);';
168
+ const ITEM_BASE_STYLE = 'padding:7px 12px;border-radius:6px;cursor:pointer;font-size:13px;';
169
+ let StoryDropdownTable = class StoryDropdownTable extends LitElement {
170
+ // Disable shadow DOM so Stencil's slot emulation can see and move
171
+ // the slotted <ul> nodes without Lit's ChildPart losing track of them.
172
+ createRenderRoot() { return this; }
173
+ constructor() {
174
+ super();
175
+ this.roleFilter = 'All';
176
+ this.statusFilter = 'All';
177
+ }
178
+ get displayRows() {
179
+ return DROPDOWN_SEED.filter(row => (this.roleFilter === 'All' || row.role === this.roleFilter) &&
180
+ (this.statusFilter === 'All' || row.status === this.statusFilter));
181
+ }
182
+ itemStyle(active) {
183
+ return `${ITEM_BASE_STYLE}background:${active ? '#f0f9ff' : 'transparent'};color:${active ? '#0369a1' : '#374151'};font-weight:${active ? '600' : '400'};`;
184
+ }
185
+ render() {
186
+ const rows = this.displayRows;
187
+ const roleOptions = ['All', 'Owner', 'Manager', 'Analyst'];
188
+ const statusOptions = ['All', 'active', 'reviewing', 'archived'];
189
+ return html `
190
+ <div style="display:flex;flex-direction:column;gap:12px;">
191
+ <div style="display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:14px;">
192
+ <span style="color:#64748b;">Active filters:</span>
193
+ ${this.roleFilter !== 'All' ? html `<span style="border-radius:9999px;background:#ecfeff;padding:2px 12px;font-size:12px;font-weight:500;color:#0e7490;">Role: ${this.roleFilter}</span>` : nothing}
194
+ ${this.statusFilter !== 'All' ? html `<span style="border-radius:9999px;background:#ecfeff;padding:2px 12px;font-size:12px;font-weight:500;color:#0e7490;">Status: ${DROPDOWN_STATUS_META[this.statusFilter]?.label}</span>` : nothing}
195
+ ${this.roleFilter === 'All' && this.statusFilter === 'All' ? html `<span style="color:#94a3b8;">없음</span>` : nothing}
196
+ ${this.roleFilter !== 'All' || this.statusFilter !== 'All' ? html `<button style="font-size:12px;color:#64748b;text-decoration:underline;background:none;border:none;cursor:pointer;" @click=${() => { this.roleFilter = 'All'; this.statusFilter = 'All'; }}>초기화</button>` : nothing}
197
+ </div>
198
+
199
+ <sd-table .columns=${DROPDOWN_COLUMNS} .rows=${rows} row-key="id" width="100%">
200
+ <sd-thead>
201
+ <ul slot="dropdown-role" style=${LISTBOX_STYLE}>
202
+ ${roleOptions.map(opt => html `
203
+ <li style=${this.itemStyle(this.roleFilter === opt)} @click=${() => { this.roleFilter = opt; }}>${opt}</li>
204
+ `)}
205
+ </ul>
206
+ <ul slot="dropdown-status" style=${LISTBOX_STYLE}>
207
+ ${statusOptions.map(opt => html `
208
+ <li style=${this.itemStyle(this.statusFilter === opt)} @click=${() => { this.statusFilter = opt; }}>
209
+ ${opt === 'All' ? 'All' : DROPDOWN_STATUS_META[opt].label}
210
+ </li>
211
+ `)}
212
+ </ul>
213
+ </sd-thead>
214
+ <sd-tbody>
215
+ ${repeat(rows, row => row.id, row => {
216
+ const meta = DROPDOWN_STATUS_META[row.status];
217
+ return html `
218
+ <sd-tr row-key=${String(row.id)} .row=${row}>
219
+ <sd-td field="status" row-key=${String(row.id)}>
220
+ <span style="display:inline-flex;min-width:88px;align-items:center;justify-content:center;border-radius:9999px;padding:4px 10px;font-size:12px;font-weight:600;background:${meta.bg};color:${meta.color};">
221
+ ${meta.label}
222
+ </span>
223
+ </sd-td>
224
+ </sd-tr>
225
+ `;
226
+ })}
227
+ </sd-tbody>
228
+ </sd-table>
229
+ </div>
230
+ `;
231
+ }
232
+ };
233
+ __decorate([
234
+ state()
235
+ ], StoryDropdownTable.prototype, "roleFilter", void 0);
236
+ __decorate([
237
+ state()
238
+ ], StoryDropdownTable.prototype, "statusFilter", void 0);
239
+ StoryDropdownTable = __decorate([
240
+ customElement('story-dropdown-table')
241
+ ], StoryDropdownTable);
242
+ export { StoryDropdownTable };
@@ -143,6 +143,27 @@ sd-thead * {
143
143
  background-color: var(--table-header-resizer-color, #CCCCCC);
144
144
  }
145
145
 
146
+ .th__dropdown-trigger {
147
+ display: flex;
148
+ align-items: center;
149
+ justify-content: center;
150
+ flex-shrink: 0;
151
+ width: 16px;
152
+ height: 16px;
153
+ padding: 0;
154
+ border: none;
155
+ background: transparent;
156
+ cursor: pointer;
157
+ color: inherit;
158
+ border-radius: 2px;
159
+ }
160
+ .th__dropdown-trigger sd-icon {
161
+ transition: transform 0.15s ease;
162
+ }
163
+ .th__dropdown-trigger--open sd-icon {
164
+ transform: rotate(180deg);
165
+ }
166
+
146
167
  .th__resizer {
147
168
  position: absolute;
148
169
  top: 50%;