@kodaris/krubble-components 1.0.9 → 1.0.10

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.
@@ -95,7 +95,7 @@
95
95
  * SPDX-License-Identifier: BSD-3-Clause
96
96
  */function e$3(e,r){return (n,s,i)=>{const o=t=>t.renderRoot?.querySelector(e)??null;return e$4(n,s,{get(){return o(this)}})}}
97
97
 
98
- var __decorate$b = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
98
+ var __decorate$c = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
99
99
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
100
100
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
101
101
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -202,13 +202,13 @@
202
202
  padding: 16px 16px 8px 16px;
203
203
  }
204
204
  `;
205
- __decorate$b([
205
+ __decorate$c([
206
206
  n$1({ type: String })
207
207
  ], exports.KRAccordion.prototype, "header", void 0);
208
- __decorate$b([
208
+ __decorate$c([
209
209
  n$1({ type: Boolean, reflect: true })
210
210
  ], exports.KRAccordion.prototype, "expanded", void 0);
211
- exports.KRAccordion = __decorate$b([
211
+ exports.KRAccordion = __decorate$c([
212
212
  t$1('kr-accordion')
213
213
  ], exports.KRAccordion);
214
214
 
@@ -225,7 +225,7 @@
225
225
  * SPDX-License-Identifier: BSD-3-Clause
226
226
  */const e$1=e$2(class extends i$1{constructor(t$1){if(super(t$1),t$1.type!==t.ATTRIBUTE||"class"!==t$1.name||t$1.strings?.length>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(t){return " "+Object.keys(t).filter(s=>t[s]).join(" ")+" "}update(s,[i]){if(void 0===this.st){this.st=new Set,void 0!==s.strings&&(this.nt=new Set(s.strings.join(" ").split(/\s/).filter(t=>""!==t)));for(const t in i)i[t]&&!this.nt?.has(t)&&this.st.add(t);return this.render(i)}const r=s.element.classList;for(const t of this.st)t in i||(r.remove(t),this.st.delete(t));for(const t in i){const s=!!i[t];s===this.st.has(t)||this.nt?.has(t)||(s?(r.add(t),this.st.add(t)):(r.remove(t),this.st.delete(t)));}return E}});
227
227
 
228
- var __decorate$a = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
228
+ var __decorate$b = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
229
229
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
230
230
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
231
231
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -409,23 +409,23 @@
409
409
  outline-offset: 2px;
410
410
  }
411
411
  `;
412
- __decorate$a([
412
+ __decorate$b([
413
413
  n$1({ type: String })
414
414
  ], exports.KRAlert.prototype, "type", void 0);
415
- __decorate$a([
415
+ __decorate$b([
416
416
  n$1({ type: String })
417
417
  ], exports.KRAlert.prototype, "header", void 0);
418
- __decorate$a([
418
+ __decorate$b([
419
419
  n$1({ type: Boolean })
420
420
  ], exports.KRAlert.prototype, "dismissible", void 0);
421
- __decorate$a([
421
+ __decorate$b([
422
422
  n$1({ type: Boolean })
423
423
  ], exports.KRAlert.prototype, "visible", void 0);
424
- exports.KRAlert = __decorate$a([
424
+ exports.KRAlert = __decorate$b([
425
425
  t$1('kr-alert')
426
426
  ], exports.KRAlert);
427
427
 
428
- var __decorate$9 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
428
+ var __decorate$a = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
429
429
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
430
430
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
431
431
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -436,6 +436,7 @@
436
436
  *
437
437
  * @slot - The button content
438
438
  * @fires click - Fired when the button is clicked
439
+ * @fires option-select - Fired when a dropdown option is selected
439
440
  */
440
441
  exports.KRButton = class KRButton extends i$2 {
441
442
  constructor() {
@@ -456,12 +457,36 @@
456
457
  * Whether the button is disabled
457
458
  */
458
459
  this.disabled = false;
460
+ /**
461
+ * Dropdown options - when provided, button becomes a dropdown
462
+ */
463
+ this.options = [];
459
464
  this._state = 'idle';
460
465
  this._stateText = '';
466
+ this._dropdownOpen = false;
467
+ this._handleHostClick = (e) => {
468
+ if (this.options.length) {
469
+ e.stopPropagation();
470
+ this._toggleDropdown();
471
+ }
472
+ };
461
473
  this._handleKeydown = (e) => {
462
474
  if (e.key === 'Enter' || e.key === ' ') {
463
475
  e.preventDefault();
464
- this.click();
476
+ if (this.options.length) {
477
+ this._toggleDropdown();
478
+ }
479
+ else {
480
+ this.click();
481
+ }
482
+ }
483
+ if (e.key === 'Escape' && this._dropdownOpen) {
484
+ this._dropdownOpen = false;
485
+ }
486
+ };
487
+ this._handleClickOutside = (e) => {
488
+ if (this._dropdownOpen && !this.contains(e.target)) {
489
+ this._dropdownOpen = false;
465
490
  }
466
491
  };
467
492
  }
@@ -470,10 +495,26 @@
470
495
  this.setAttribute('role', 'button');
471
496
  this.setAttribute('tabindex', '0');
472
497
  this.addEventListener('keydown', this._handleKeydown);
498
+ this.addEventListener('click', this._handleHostClick);
499
+ document.addEventListener('click', this._handleClickOutside);
473
500
  }
474
501
  disconnectedCallback() {
475
502
  super.disconnectedCallback();
476
503
  this.removeEventListener('keydown', this._handleKeydown);
504
+ this.removeEventListener('click', this._handleHostClick);
505
+ document.removeEventListener('click', this._handleClickOutside);
506
+ }
507
+ _toggleDropdown() {
508
+ this._dropdownOpen = !this._dropdownOpen;
509
+ }
510
+ _handleOptionClick(option, e) {
511
+ e.stopPropagation();
512
+ this._dropdownOpen = false;
513
+ this.dispatchEvent(new CustomEvent('option-select', {
514
+ detail: { id: option.id, label: option.label },
515
+ bubbles: true,
516
+ composed: true
517
+ }));
477
518
  }
478
519
  /**
479
520
  * Shows a loading spinner and disables the button.
@@ -530,15 +571,27 @@
530
571
  this.classList.toggle('kr-button--large', this.size === 'large');
531
572
  }
532
573
  render() {
574
+ const hasOptions = this.options.length > 0;
533
575
  return b `
534
576
  <slot></slot>
577
+ ${hasOptions ? b `<svg class="caret" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24" fill="currentColor"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/></svg>` : A}
535
578
  ${this._state !== 'idle'
536
579
  ? b `<span class="state-overlay">
537
580
  ${this._state === 'loading'
538
581
  ? b `<span class="spinner"></span>`
539
582
  : this._stateText}
540
583
  </span>`
541
- : ''}
584
+ : A}
585
+ ${hasOptions ? b `
586
+ <div class="dropdown ${this._dropdownOpen ? 'open' : ''}">
587
+ ${this.options.map(option => b `
588
+ <button
589
+ class="dropdown-item"
590
+ @click=${(e) => this._handleOptionClick(option, e)}
591
+ >${option.label}</button>
592
+ `)}
593
+ </div>
594
+ ` : A}
542
595
  `;
543
596
  }
544
597
  };
@@ -687,26 +740,90 @@
687
740
  height: 16px;
688
741
  border-width: 3px;
689
742
  }
743
+
744
+ /* Dropdown */
745
+ .dropdown {
746
+ position: absolute;
747
+ top: 100%;
748
+ left: 0;
749
+ margin-top: 4px;
750
+ min-width: 100%;
751
+ background: white;
752
+ border: 1px solid #9ba7b6;
753
+ border-radius: 8px;
754
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
755
+ padding: 8px 0;
756
+ z-index: 100;
757
+ display: none;
758
+ transform-origin: top;
759
+ }
760
+
761
+ .dropdown.open {
762
+ display: block;
763
+ animation: dropdown-fade-in 150ms ease-out;
764
+ }
765
+
766
+ @keyframes dropdown-fade-in {
767
+ from {
768
+ opacity: 0;
769
+ transform: translateY(-4px);
770
+ }
771
+ to {
772
+ opacity: 1;
773
+ transform: translateY(0);
774
+ }
775
+ }
776
+
777
+ .dropdown-item {
778
+ display: block;
779
+ width: 100%;
780
+ padding: 8px 16px;
781
+ text-align: left;
782
+ background: none;
783
+ border: none;
784
+ font-size: 14px;
785
+ color: #374151;
786
+ cursor: pointer;
787
+ font-family: inherit;
788
+ white-space: nowrap;
789
+ }
790
+
791
+ .dropdown-item:hover {
792
+ background: #f3f4f6;
793
+ }
794
+
795
+ /* Caret for dropdown button */
796
+ .caret {
797
+ margin-left: 4px;
798
+ margin-right: -6px;
799
+ display: flex;
800
+ }
690
801
  `;
691
- __decorate$9([
802
+ __decorate$a([
692
803
  n$1({ type: String, reflect: true })
693
804
  ], exports.KRButton.prototype, "variant", void 0);
694
- __decorate$9([
805
+ __decorate$a([
695
806
  n$1({ type: String, reflect: true })
696
807
  ], exports.KRButton.prototype, "color", void 0);
697
- __decorate$9([
808
+ __decorate$a([
698
809
  n$1({ type: String, reflect: true })
699
810
  ], exports.KRButton.prototype, "size", void 0);
700
- __decorate$9([
811
+ __decorate$a([
701
812
  n$1({ type: Boolean, reflect: true })
702
813
  ], exports.KRButton.prototype, "disabled", void 0);
703
- __decorate$9([
814
+ __decorate$a([
815
+ n$1({ type: Array })
816
+ ], exports.KRButton.prototype, "options", void 0);
817
+ __decorate$a([
704
818
  r$1()
705
819
  ], exports.KRButton.prototype, "_state", void 0);
706
- __decorate$9([
820
+ __decorate$a([
707
821
  r$1()
708
822
  ], exports.KRButton.prototype, "_stateText", void 0);
709
- exports.KRButton = __decorate$9([
823
+ __decorate$a([
824
+ r$1()
825
+ ], exports.KRButton.prototype, "_dropdownOpen", void 0);
826
+ exports.KRButton = __decorate$a([
710
827
  t$1('kr-button')
711
828
  ], exports.KRButton);
712
829
 
@@ -716,7 +833,7 @@
716
833
  * SPDX-License-Identifier: BSD-3-Clause
717
834
  */class e extends i$1{constructor(i){if(super(i),this.it=A,i.type!==t.CHILD)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(r){if(r===A||null==r)return this._t=void 0,this.it=r;if(r===E)return r;if("string"!=typeof r)throw Error(this.constructor.directiveName+"() called with a non-string value");if(r===this.it)return this._t;this.it=r;const s=[r];return s.raw=s,this._t={_$litType$:this.constructor.resultType,strings:s,values:[]}}}e.directiveName="unsafeHTML",e.resultType=1;const o$2=e$2(e);
718
835
 
719
- var __decorate$8 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
836
+ var __decorate$9 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
720
837
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
721
838
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
722
839
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -973,23 +1090,23 @@
973
1090
  color: #fab387;
974
1091
  }
975
1092
  `;
976
- __decorate$8([
1093
+ __decorate$9([
977
1094
  n$1({ type: String })
978
1095
  ], exports.KRCodeDemo.prototype, "language", void 0);
979
- __decorate$8([
1096
+ __decorate$9([
980
1097
  n$1({ type: String })
981
1098
  ], exports.KRCodeDemo.prototype, "code", void 0);
982
- __decorate$8([
1099
+ __decorate$9([
983
1100
  r$1()
984
1101
  ], exports.KRCodeDemo.prototype, "activeTab", void 0);
985
- __decorate$8([
1102
+ __decorate$9([
986
1103
  r$1()
987
1104
  ], exports.KRCodeDemo.prototype, "copied", void 0);
988
- exports.KRCodeDemo = __decorate$8([
1105
+ exports.KRCodeDemo = __decorate$9([
989
1106
  t$1('kr-code-demo')
990
1107
  ], exports.KRCodeDemo);
991
1108
 
992
- var __decorate$7 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
1109
+ var __decorate$8 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
993
1110
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
994
1111
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
995
1112
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -1158,14 +1275,14 @@
1158
1275
  margin: 4px 0;
1159
1276
  }
1160
1277
  `;
1161
- __decorate$7([
1278
+ __decorate$8([
1162
1279
  r$1()
1163
1280
  ], exports.KRContextMenu.prototype, "items", void 0);
1164
- exports.KRContextMenu = __decorate$7([
1281
+ exports.KRContextMenu = __decorate$8([
1165
1282
  t$1('kr-context-menu')
1166
1283
  ], exports.KRContextMenu);
1167
1284
 
1168
- var __decorate$6 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
1285
+ var __decorate$7 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
1169
1286
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1170
1287
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1171
1288
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -1297,14 +1414,14 @@
1297
1414
  overflow: auto;
1298
1415
  }
1299
1416
  `;
1300
- __decorate$6([
1417
+ __decorate$7([
1301
1418
  r$1()
1302
1419
  ], exports.KRDialog.prototype, "contentElement", void 0);
1303
- exports.KRDialog = __decorate$6([
1420
+ exports.KRDialog = __decorate$7([
1304
1421
  t$1('kr-dialog')
1305
1422
  ], exports.KRDialog);
1306
1423
 
1307
- var __decorate$5 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
1424
+ var __decorate$6 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
1308
1425
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1309
1426
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1310
1427
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -1601,19 +1718,19 @@
1601
1718
  `;
1602
1719
  /** Track active snackbars for stacking */
1603
1720
  exports.KRSnackbar.activeSnackbars = [];
1604
- __decorate$5([
1721
+ __decorate$6([
1605
1722
  n$1({ type: String })
1606
1723
  ], exports.KRSnackbar.prototype, "type", void 0);
1607
- __decorate$5([
1724
+ __decorate$6([
1608
1725
  n$1({ type: String })
1609
1726
  ], exports.KRSnackbar.prototype, "title", void 0);
1610
- __decorate$5([
1727
+ __decorate$6([
1611
1728
  n$1({ type: String })
1612
1729
  ], exports.KRSnackbar.prototype, "message", void 0);
1613
- __decorate$5([
1730
+ __decorate$6([
1614
1731
  n$1({ type: Number })
1615
1732
  ], exports.KRSnackbar.prototype, "duration", void 0);
1616
- exports.KRSnackbar = KRSnackbar_1 = __decorate$5([
1733
+ exports.KRSnackbar = KRSnackbar_1 = __decorate$6([
1617
1734
  t$1('kr-snackbar')
1618
1735
  ], exports.KRSnackbar);
1619
1736
 
@@ -1623,7 +1740,7 @@
1623
1740
  * SPDX-License-Identifier: BSD-3-Clause
1624
1741
  */const n="important",i=" !"+n,o$1=e$2(class extends i$1{constructor(t$1){if(super(t$1),t$1.type!==t.ATTRIBUTE||"style"!==t$1.name||t$1.strings?.length>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(t){return Object.keys(t).reduce((e,r)=>{const s=t[r];return null==s?e:e+`${r=r.includes("-")?r:r.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${s};`},"")}update(e,[r]){const{style:s}=e.element;if(void 0===this.ft)return this.ft=new Set(Object.keys(r)),this.render(r);for(const t of this.ft)null==r[t]&&(this.ft.delete(t),t.includes("-")?s.removeProperty(t):s[t]=null);for(const t in r){const e=r[t];if(null!=e){this.ft.add(t);const r="string"==typeof e&&e.endsWith(i);t.includes("-")||r?s.setProperty(t,r?e.slice(0,-11):e,r?n:""):s[t]=e;}}return E}});
1625
1742
 
1626
- var __decorate$4 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
1743
+ var __decorate$5 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
1627
1744
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1628
1745
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1629
1746
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -1798,7 +1915,9 @@
1798
1915
  }
1799
1916
 
1800
1917
  .header {
1918
+ height: 44px;
1801
1919
  display: flex;
1920
+ flex-shrink: 0;
1802
1921
  align-items: stretch;
1803
1922
  border-bottom: 1px solid #e5e7eb;
1804
1923
  overflow-x: auto;
@@ -1923,20 +2042,20 @@
1923
2042
  }
1924
2043
  `,
1925
2044
  ];
1926
- __decorate$4([
2045
+ __decorate$5([
1927
2046
  n$1({ type: String, attribute: 'active-tab-id' })
1928
2047
  ], exports.KRTabGroup.prototype, "activeTabId", void 0);
1929
- __decorate$4([
2048
+ __decorate$5([
1930
2049
  n$1({ type: Boolean })
1931
2050
  ], exports.KRTabGroup.prototype, "justified", void 0);
1932
- __decorate$4([
2051
+ __decorate$5([
1933
2052
  n$1({ type: String, reflect: true })
1934
2053
  ], exports.KRTabGroup.prototype, "size", void 0);
1935
- exports.KRTabGroup = __decorate$4([
2054
+ exports.KRTabGroup = __decorate$5([
1936
2055
  t$1('kr-tab-group')
1937
2056
  ], exports.KRTabGroup);
1938
2057
 
1939
- var __decorate$3 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
2058
+ var __decorate$4 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
1940
2059
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1941
2060
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1942
2061
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -2007,34 +2126,1170 @@
2007
2126
  }
2008
2127
  `,
2009
2128
  ];
2010
- __decorate$3([
2129
+ __decorate$4([
2011
2130
  n$1({ type: String, reflect: true })
2012
2131
  ], exports.KRTab.prototype, "id", void 0);
2013
- __decorate$3([
2132
+ __decorate$4([
2014
2133
  n$1({ type: String })
2015
2134
  ], exports.KRTab.prototype, "title", void 0);
2016
- __decorate$3([
2135
+ __decorate$4([
2017
2136
  n$1({ type: String })
2018
2137
  ], exports.KRTab.prototype, "badge", void 0);
2019
- __decorate$3([
2138
+ __decorate$4([
2020
2139
  n$1({ type: String, attribute: 'badge-background' })
2021
2140
  ], exports.KRTab.prototype, "badgeBackground", void 0);
2022
- __decorate$3([
2141
+ __decorate$4([
2023
2142
  n$1({ type: String, attribute: 'badge-color' })
2024
2143
  ], exports.KRTab.prototype, "badgeColor", void 0);
2025
- __decorate$3([
2144
+ __decorate$4([
2026
2145
  n$1({ type: Boolean })
2027
2146
  ], exports.KRTab.prototype, "disabled", void 0);
2028
- __decorate$3([
2147
+ __decorate$4([
2029
2148
  n$1({ type: Boolean })
2030
2149
  ], exports.KRTab.prototype, "dismissible", void 0);
2031
- __decorate$3([
2150
+ __decorate$4([
2032
2151
  n$1({ type: Boolean, reflect: true })
2033
2152
  ], exports.KRTab.prototype, "active", void 0);
2034
- exports.KRTab = __decorate$3([
2153
+ exports.KRTab = __decorate$4([
2035
2154
  t$1('kr-tab')
2036
2155
  ], exports.KRTab);
2037
2156
 
2157
+ var __decorate$3 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
2158
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2159
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2160
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2161
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2162
+ };
2163
+ // === Solr Utilities ===
2164
+ const SOLR_RESERVED_CHARS = [
2165
+ '"', '+', '-', '&&', '||', '!', '(', ')', '{',
2166
+ '}', '[', ']', '^', '~', '*', '?', ':'
2167
+ ];
2168
+ const SOLR_RESERVED_CHARS_REPLACEMENT = [
2169
+ '\\"', '\\+', '\\-', '\\&\\&', '\\|\\|', '\\!', '\\(', '\\)', '\\{',
2170
+ '\\}', '\\[', '\\]', '\\^', '\\~', '\\*', '\\?', '\\:'
2171
+ ];
2172
+ function escapeSolrQuery(query) {
2173
+ let escaped = query;
2174
+ for (let i = 0; i < SOLR_RESERVED_CHARS.length; i++) {
2175
+ escaped = escaped.split(SOLR_RESERVED_CHARS[i]).join(SOLR_RESERVED_CHARS_REPLACEMENT[i]);
2176
+ }
2177
+ return escaped;
2178
+ }
2179
+ exports.KRTable = class KRTable extends i$2 {
2180
+ constructor() {
2181
+ super(...arguments);
2182
+ /**
2183
+ * Internal flag to switch between scroll edge modes:
2184
+ * - 'overlay': Fixed padding with overlay elements that hide content at edges (scrollbar at viewport edge)
2185
+ * - 'edge': Padding scrolls with content, allowing table to reach edges when scrolling
2186
+ */
2187
+ this._scrollStyle = 'edge';
2188
+ this._data = [];
2189
+ this._dataState = 'idle';
2190
+ this._page = 1;
2191
+ this._pageSize = 50;
2192
+ this._totalItems = 0;
2193
+ this._totalPages = 0;
2194
+ this._searchQuery = '';
2195
+ this._canScrollLeft = false;
2196
+ this._canScrollRight = false;
2197
+ this._canScrollHorizontal = false;
2198
+ this._columnPickerOpen = false;
2199
+ this._displayedColumns = [];
2200
+ this._columnWidths = new Map();
2201
+ this._resizing = null;
2202
+ this._resizeObserver = null;
2203
+ this._searchPositionLocked = false;
2204
+ this.def = { columns: [] };
2205
+ this._handleClickOutsideColumnPicker = (e) => {
2206
+ if (!this._columnPickerOpen)
2207
+ return;
2208
+ const path = e.composedPath();
2209
+ const picker = this.shadowRoot?.querySelector('.column-picker-wrapper');
2210
+ if (picker && !path.includes(picker)) {
2211
+ this._columnPickerOpen = false;
2212
+ }
2213
+ };
2214
+ this._handleResizeMove = (e) => {
2215
+ if (!this._resizing)
2216
+ return;
2217
+ this._columnWidths.set(this._resizing.columnId, Math.max(50, this._resizing.startWidth + (e.clientX - this._resizing.startX)));
2218
+ this.requestUpdate();
2219
+ };
2220
+ this._handleResizeEnd = () => {
2221
+ this._resizing = null;
2222
+ document.removeEventListener('mousemove', this._handleResizeMove);
2223
+ document.removeEventListener('mouseup', this._handleResizeEnd);
2224
+ };
2225
+ }
2226
+ connectedCallback() {
2227
+ super.connectedCallback();
2228
+ this.classList.toggle('kr-table--scroll-overlay', this._scrollStyle === 'overlay');
2229
+ this.classList.toggle('kr-table--scroll-edge', this._scrollStyle === 'edge');
2230
+ this._fetch();
2231
+ this._initRefresh();
2232
+ document.addEventListener('click', this._handleClickOutsideColumnPicker);
2233
+ this._resizeObserver = new ResizeObserver(() => {
2234
+ // Unlock and recalculate on resize since layout changes
2235
+ this._searchPositionLocked = false;
2236
+ this._updateSearchPosition();
2237
+ });
2238
+ this._resizeObserver.observe(this);
2239
+ }
2240
+ disconnectedCallback() {
2241
+ super.disconnectedCallback();
2242
+ clearInterval(this._refreshTimer);
2243
+ document.removeEventListener('click', this._handleClickOutsideColumnPicker);
2244
+ this._resizeObserver?.disconnect();
2245
+ }
2246
+ updated(changedProperties) {
2247
+ if (changedProperties.has('def')) {
2248
+ this._displayedColumns = this.def.displayedColumns || this.def.columns.map(c => c.id);
2249
+ this._fetch();
2250
+ this._initRefresh();
2251
+ }
2252
+ this._updateScrollFlags();
2253
+ }
2254
+ // ----------------------------------------------------------------------------
2255
+ // Public Interface
2256
+ // ----------------------------------------------------------------------------
2257
+ refresh() {
2258
+ this._fetch();
2259
+ }
2260
+ goToPrevPage() {
2261
+ if (this._page > 1) {
2262
+ this._page--;
2263
+ this._fetch();
2264
+ }
2265
+ }
2266
+ goToNextPage() {
2267
+ if (this._page < this._totalPages) {
2268
+ this._page++;
2269
+ this._fetch();
2270
+ }
2271
+ }
2272
+ goToPage(page) {
2273
+ if (page >= 1 && page <= this._totalPages) {
2274
+ this._page = page;
2275
+ this._fetch();
2276
+ }
2277
+ }
2278
+ // ----------------------------------------------------------------------------
2279
+ // Data Fetching
2280
+ // ----------------------------------------------------------------------------
2281
+ /**
2282
+ * Fetches data from the API and updates the table.
2283
+ * Shows a loading spinner while fetching, then displays rows on success
2284
+ * or an error snackbar on failure.
2285
+ * Request/response format depends on dataSource.mode (solr, opensearch, db).
2286
+ */
2287
+ _fetch() {
2288
+ if (!this.def.dataSource)
2289
+ return;
2290
+ this._dataState = 'loading';
2291
+ // Build request based on mode
2292
+ let request;
2293
+ switch (this.def.dataSource.mode) {
2294
+ case 'opensearch':
2295
+ throw Error('Opensearch not supported yet');
2296
+ case 'db':
2297
+ throw Error('DB not supported yet');
2298
+ default: // solr
2299
+ request = {
2300
+ page: this._page - 1,
2301
+ size: this._pageSize,
2302
+ sorts: [],
2303
+ filterFields: [],
2304
+ queryFields: [],
2305
+ facetFields: []
2306
+ };
2307
+ if (this._searchQuery?.trim().length) {
2308
+ request.queryFields.push({
2309
+ name: '_text_',
2310
+ operation: 'IS',
2311
+ value: escapeSolrQuery(this._searchQuery)
2312
+ });
2313
+ }
2314
+ }
2315
+ this.def.dataSource.fetch(request)
2316
+ .then(response => {
2317
+ // Parse response based on mode
2318
+ switch (this.def.dataSource?.mode) {
2319
+ case 'opensearch': {
2320
+ throw Error('Opensearch not supported yet');
2321
+ }
2322
+ case 'db': {
2323
+ throw Error('DB not supported yet');
2324
+ }
2325
+ default: { // solr
2326
+ const res = response;
2327
+ this._data = res.data.content;
2328
+ this._totalItems = res.data.totalElements;
2329
+ this._totalPages = res.data.totalPages;
2330
+ this._pageSize = res.data.size;
2331
+ }
2332
+ }
2333
+ this._dataState = 'success';
2334
+ this._updateSearchPosition();
2335
+ })
2336
+ .catch(err => {
2337
+ this._dataState = 'error';
2338
+ exports.KRSnackbar.show({
2339
+ message: err instanceof Error ? err.message : 'Failed to load data',
2340
+ type: 'error'
2341
+ });
2342
+ });
2343
+ }
2344
+ /**
2345
+ * Sets up auto-refresh so the table automatically fetches fresh data
2346
+ * at a regular interval (useful for dashboards, monitoring views).
2347
+ * Configured via def.refreshInterval in milliseconds.
2348
+ */
2349
+ _initRefresh() {
2350
+ clearInterval(this._refreshTimer);
2351
+ if (this.def.refreshInterval && this.def.refreshInterval > 0) {
2352
+ this._refreshTimer = window.setInterval(() => {
2353
+ this._fetch();
2354
+ }, this.def.refreshInterval);
2355
+ }
2356
+ }
2357
+ _handleSearch(e) {
2358
+ const input = e.target;
2359
+ this._searchQuery = input.value;
2360
+ this._page = 1;
2361
+ this._fetch();
2362
+ }
2363
+ /**
2364
+ * Updates search position to be centered with equal gaps from title and tools.
2365
+ * On first call: resets to flex centering, measures position, then locks with fixed margin.
2366
+ * Subsequent calls are ignored unless _searchPositionLocked is reset (e.g., on resize).
2367
+ */
2368
+ _updateSearchPosition() {
2369
+ // Skip if already locked (prevents shifts on pagination changes)
2370
+ if (this._searchPositionLocked)
2371
+ return;
2372
+ const search = this.shadowRoot?.querySelector('.search');
2373
+ const searchField = search?.querySelector('.search-field');
2374
+ if (!search || !searchField)
2375
+ return;
2376
+ // Reset to flex centering
2377
+ search.style.justifyContent = 'center';
2378
+ searchField.style.marginLeft = '';
2379
+ requestAnimationFrame(() => {
2380
+ const searchRect = search.getBoundingClientRect();
2381
+ const fieldRect = searchField.getBoundingClientRect();
2382
+ // Calculate how far from the left of search container the field currently is
2383
+ const currentOffset = fieldRect.left - searchRect.left;
2384
+ // Lock position: switch to flex-start and use fixed margin
2385
+ search.style.justifyContent = 'flex-start';
2386
+ searchField.style.marginLeft = `${currentOffset}px`;
2387
+ // Mark as locked so pagination changes don't shift the search
2388
+ this._searchPositionLocked = true;
2389
+ });
2390
+ }
2391
+ // ----------------------------------------------------------------------------
2392
+ // Columns
2393
+ // ----------------------------------------------------------------------------
2394
+ _toggleColumnPicker() {
2395
+ this._columnPickerOpen = !this._columnPickerOpen;
2396
+ }
2397
+ _toggleColumn(columnId) {
2398
+ if (this._displayedColumns.includes(columnId)) {
2399
+ this._displayedColumns = this._displayedColumns.filter(id => id !== columnId);
2400
+ }
2401
+ else {
2402
+ this._displayedColumns = [...this._displayedColumns, columnId];
2403
+ }
2404
+ }
2405
+ // When a user toggles a column on via the column picker, it gets appended
2406
+ // to _displayedColumns. By mapping over _displayedColumns (not def.columns),
2407
+ // the new column appears at the right edge of the table instead of jumping
2408
+ // back to its original position in the column definition.
2409
+ getDisplayedColumns() {
2410
+ return this._displayedColumns.map(id => this.def.columns.find(col => col.id === id));
2411
+ }
2412
+ // ----------------------------------------------------------------------------
2413
+ // Scrolling
2414
+ // ----------------------------------------------------------------------------
2415
+ /**
2416
+ * Scroll event handler that updates scroll flags in real-time as user scrolls.
2417
+ * Updates shadow indicators to show if more content exists left/right.
2418
+ */
2419
+ _handleScroll(e) {
2420
+ const container = e.target;
2421
+ this._canScrollLeft = container.scrollLeft > 0;
2422
+ this._canScrollRight = container.scrollLeft < container.scrollWidth - container.clientWidth - 1;
2423
+ }
2424
+ /**
2425
+ * Updates scroll state flags for the table content container.
2426
+ * - _canScrollLeft: true if scrolled right (can scroll back left)
2427
+ * - _canScrollRight: true if more content exists to the right
2428
+ * - _canScrollHorizontal: true if content is wider than container
2429
+ * These flags control scroll shadow indicators and CSS classes.
2430
+ */
2431
+ _updateScrollFlags() {
2432
+ const container = this.shadowRoot?.querySelector('.content');
2433
+ if (container) {
2434
+ this._canScrollLeft = container.scrollLeft > 0;
2435
+ this._canScrollRight = container.scrollWidth > container.clientWidth && container.scrollLeft < container.scrollWidth - container.clientWidth - 1;
2436
+ this._canScrollHorizontal = container.scrollWidth > container.clientWidth;
2437
+ }
2438
+ this.classList.toggle('kr-table--scroll-left-available', this._canScrollLeft);
2439
+ this.classList.toggle('kr-table--scroll-right-available', this._canScrollRight);
2440
+ this.classList.toggle('kr-table--scroll-horizontal-available', this._canScrollHorizontal);
2441
+ }
2442
+ // ----------------------------------------------------------------------------
2443
+ // Column Resizing
2444
+ // ----------------------------------------------------------------------------
2445
+ _handleResizeStart(e, columnId) {
2446
+ e.preventDefault();
2447
+ const column = this.def.columns.find(c => c.id === columnId);
2448
+ this._resizing = {
2449
+ columnId,
2450
+ startX: e.clientX,
2451
+ startWidth: this._columnWidths.get(columnId) || parseInt(column?.width || '150', 10)
2452
+ };
2453
+ document.addEventListener('mousemove', this._handleResizeMove);
2454
+ document.addEventListener('mouseup', this._handleResizeEnd);
2455
+ }
2456
+ // ----------------------------------------------------------------------------
2457
+ // Header
2458
+ // ----------------------------------------------------------------------------
2459
+ _handleAction(action) {
2460
+ this.dispatchEvent(new CustomEvent('action', {
2461
+ detail: { action: action.id },
2462
+ bubbles: true,
2463
+ composed: true
2464
+ }));
2465
+ }
2466
+ // ----------------------------------------------------------------------------
2467
+ // Rendering
2468
+ // ----------------------------------------------------------------------------
2469
+ _renderCellContent(column, row) {
2470
+ const value = row[column.id];
2471
+ if (column.render) {
2472
+ return column.render(row);
2473
+ }
2474
+ if (value === null || value === undefined) {
2475
+ return '';
2476
+ }
2477
+ switch (column.type) {
2478
+ case 'number':
2479
+ return typeof value === 'number' ? value.toLocaleString() : String(value);
2480
+ case 'currency':
2481
+ return typeof value === 'number'
2482
+ ? value.toLocaleString('en-US', { style: 'currency', currency: 'USD' })
2483
+ : String(value);
2484
+ case 'date':
2485
+ return value instanceof Date
2486
+ ? value.toLocaleDateString()
2487
+ : new Date(value).toLocaleDateString();
2488
+ case 'boolean':
2489
+ if (value === true)
2490
+ return 'Yes';
2491
+ if (value === false)
2492
+ return 'No';
2493
+ return '';
2494
+ default:
2495
+ return String(value);
2496
+ }
2497
+ }
2498
+ /**
2499
+ * Returns CSS classes for a header cell based on column config.
2500
+ */
2501
+ _getHeaderCellClasses(column, index) {
2502
+ return {
2503
+ 'header-cell': true,
2504
+ 'header-cell--align-center': column.align === 'center',
2505
+ 'header-cell--align-right': column.align === 'right',
2506
+ 'header-cell--sticky-left': column.sticky === 'left',
2507
+ 'header-cell--sticky-left-last': column.sticky === 'left' &&
2508
+ !this.getDisplayedColumns().slice(index + 1).some(c => c.sticky === 'left'),
2509
+ 'header-cell--sticky-right': column.sticky === 'right',
2510
+ 'header-cell--sticky-right-first': column.sticky === 'right' &&
2511
+ !this.getDisplayedColumns().slice(0, index).some(c => c.sticky === 'right')
2512
+ };
2513
+ }
2514
+ /**
2515
+ * Returns CSS classes for a table cell based on column config:
2516
+ * - Alignment (center, right)
2517
+ * - Sticky positioning (left, right)
2518
+ * - Border classes for the last left-sticky or first right-sticky column
2519
+ */
2520
+ _getCellClasses(column, index) {
2521
+ return {
2522
+ 'cell': true,
2523
+ 'cell--align-center': column.align === 'center',
2524
+ 'cell--align-right': column.align === 'right',
2525
+ 'cell--sticky-left': column.sticky === 'left',
2526
+ 'cell--sticky-left-last': column.sticky === 'left' &&
2527
+ !this.getDisplayedColumns().slice(index + 1).some(c => c.sticky === 'left'),
2528
+ 'cell--sticky-right': column.sticky === 'right',
2529
+ 'cell--sticky-right-first': column.sticky === 'right' &&
2530
+ !this.getDisplayedColumns().slice(0, index).some(c => c.sticky === 'right')
2531
+ };
2532
+ }
2533
+ /**
2534
+ * Returns inline styles for a table cell:
2535
+ * - Width (from column config or default 150px)
2536
+ * - Min-width (if specified)
2537
+ * - Left/right offset for sticky columns (calculated from widths of preceding sticky columns)
2538
+ */
2539
+ _getCellStyle(column, index) {
2540
+ const styles = {};
2541
+ const isLastColumn = index === this.getDisplayedColumns().length - 1;
2542
+ const customWidth = this._columnWidths.get(column.id);
2543
+ const width = customWidth ? `${customWidth}px` : (column.width || '150px');
2544
+ if (isLastColumn && !customWidth) {
2545
+ styles.flex = '1';
2546
+ styles.minWidth = column.width || '150px';
2547
+ styles.marginRight = '24px';
2548
+ }
2549
+ else {
2550
+ styles.width = width;
2551
+ }
2552
+ if (column.minWidth)
2553
+ styles.minWidth = column.minWidth;
2554
+ if (column.sticky === 'left') {
2555
+ let leftOffset = 0;
2556
+ for (let i = 0; i < index; i++) {
2557
+ if (this.getDisplayedColumns()[i].sticky === 'left') {
2558
+ leftOffset += parseInt(this.getDisplayedColumns()[i].width || '150', 10);
2559
+ }
2560
+ }
2561
+ styles.left = `${leftOffset}px`;
2562
+ }
2563
+ if (column.sticky === 'right') {
2564
+ let rightOffset = 0;
2565
+ for (let i = index + 1; i < this.getDisplayedColumns().length; i++) {
2566
+ if (this.getDisplayedColumns()[i].sticky === 'right') {
2567
+ rightOffset += parseInt(this.getDisplayedColumns()[i].width || '150', 10);
2568
+ }
2569
+ }
2570
+ styles.right = `${rightOffset}px`;
2571
+ }
2572
+ return styles;
2573
+ }
2574
+ /**
2575
+ * Renders the pagination controls:
2576
+ * - Previous page arrow (disabled on first page)
2577
+ * - Range text showing "1-50 of 150" format
2578
+ * - Next page arrow (disabled on last page)
2579
+ *
2580
+ * Hidden when there's no data or all data fits on one page.
2581
+ */
2582
+ _renderPagination() {
2583
+ const start = (this._page - 1) * this._pageSize + 1;
2584
+ const end = Math.min(this._page * this._pageSize, this._totalItems);
2585
+ return b `
2586
+ <div class="pagination">
2587
+ <span
2588
+ class="pagination-icon ${this._page === 1 ? 'pagination-icon--disabled' : ''}"
2589
+ @click=${this.goToPrevPage}
2590
+ >
2591
+ <svg viewBox="0 0 24 24" fill="currentColor"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/></svg>
2592
+ </span>
2593
+ <span class="pagination-info">${start}-${end} of ${this._totalItems}</span>
2594
+ <span
2595
+ class="pagination-icon ${this._page === this._totalPages ? 'pagination-icon--disabled' : ''}"
2596
+ @click=${this.goToNextPage}
2597
+ >
2598
+ <svg viewBox="0 0 24 24" fill="currentColor"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
2599
+ </span>
2600
+ </div>
2601
+ `;
2602
+ }
2603
+ /**
2604
+ * Renders the header toolbar containing:
2605
+ * - Title (left)
2606
+ * - Search bar with view selector dropdown (center)
2607
+ * - Tools (right): page navigation, refresh button, column visibility picker, actions dropdown
2608
+ *
2609
+ * Hidden when there's no title, no actions, and data fits on one page.
2610
+ */
2611
+ _renderHeader() {
2612
+ if (!this.def.title && !this.def.actions?.length && this._totalPages <= 1) {
2613
+ return A;
2614
+ }
2615
+ return b `
2616
+ <div class="header">
2617
+ <div class="title">${this.def.title ?? ''}</div>
2618
+ <div class="search">
2619
+ <!-- TODO: Saved views dropdown
2620
+ <div class="views">
2621
+ <span>Default View</span>
2622
+ <svg viewBox="0 0 24 24" fill="currentColor"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/></svg>
2623
+ </div>
2624
+ -->
2625
+ <div class="search-field">
2626
+ <svg class="search-icon" viewBox="0 -960 960 960" fill="currentColor"><path d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/></svg>
2627
+ <input
2628
+ type="text"
2629
+ class="search-input"
2630
+ placeholder="Search..."
2631
+ .value=${this._searchQuery}
2632
+ @input=${this._handleSearch}
2633
+ />
2634
+ </div>
2635
+ </div>
2636
+ <div class="tools">
2637
+ ${this._renderPagination()}
2638
+ <span class="refresh" title="Refresh" @click=${() => this.refresh()}>
2639
+ <svg viewBox="0 -960 960 960" fill="currentColor"><path d="M480-160q-134 0-227-93t-93-227q0-134 93-227t227-93q69 0 132 28.5T720-690v-110h80v280H520v-80h168q-32-56-87.5-88T480-720q-100 0-170 70t-70 170q0 100 70 170t170 70q77 0 139-44t87-116h84q-28 106-114 173t-196 67Z"/></svg>
2640
+ </span>
2641
+ <div class="column-picker-wrapper">
2642
+ <span class="header-icon" title="Columns" @click=${this._toggleColumnPicker}>
2643
+ <svg viewBox="0 -960 960 960" fill="currentColor"><path d="M121-280v-400q0-33 23.5-56.5T201-760h559q33 0 56.5 23.5T840-680v400q0 33-23.5 56.5T760-200H201q-33 0-56.5-23.5T121-280Zm79 0h133v-400H200v400Zm213 0h133v-400H413v400Zm213 0h133v-400H626v400Z"/></svg>
2644
+ </span>
2645
+ <div class="column-picker ${this._columnPickerOpen ? 'open' : ''}">
2646
+ ${[...this.def.columns].sort((a, b) => (a.label ?? a.id).localeCompare(b.label ?? b.id)).map(col => b `
2647
+ <div class="column-picker-item" @click=${() => this._toggleColumn(col.id)}>
2648
+ <div class="column-picker-checkbox ${this._displayedColumns.includes(col.id) ? 'checked' : ''}">
2649
+ <svg viewBox="0 0 24 24" fill="currentColor"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
2650
+ </div>
2651
+ <span class="column-picker-label">${col.label ?? col.id}</span>
2652
+ </div>
2653
+ `)}
2654
+ </div>
2655
+ </div>
2656
+ ${this.def.actions?.length ? b `
2657
+ <kr-button
2658
+ class="actions"
2659
+ .options=${this.def.actions.map(a => ({ id: a.id, label: a.label }))}
2660
+ @option-select=${(e) => this._handleAction({ id: e.detail.id, label: e.detail.label })}
2661
+ >
2662
+ Actions
2663
+ </kr-button>
2664
+ ` : A}
2665
+ </div>
2666
+ </div>
2667
+ `;
2668
+ }
2669
+ /** Renders status message (loading, error, empty) */
2670
+ _renderStatus() {
2671
+ if (this._dataState === 'loading' && this._data.length === 0) {
2672
+ return b `<div class="status">Loading...</div>`;
2673
+ }
2674
+ if (this._dataState === 'error' && this._data.length === 0) {
2675
+ return b `<div class="status status--error">Error loading data</div>`;
2676
+ }
2677
+ if (this._data.length === 0) {
2678
+ return b `<div class="status">No data available</div>`;
2679
+ }
2680
+ return A;
2681
+ }
2682
+ /** Renders the scrollable data grid with column headers and rows. */
2683
+ _renderTable() {
2684
+ return b `
2685
+ <div class="wrapper">
2686
+ <div class="overlay-left"></div>
2687
+ <div class="overlay-right"></div>
2688
+ ${this._renderStatus()}
2689
+ <div class="content" @scroll=${this._handleScroll}>
2690
+ <div class="table">
2691
+ <div class="header-row">
2692
+ ${this.getDisplayedColumns().map((col, i) => b `
2693
+ <div
2694
+ class=${e$1(this._getHeaderCellClasses(col, i))}
2695
+ style=${o$1(this._getCellStyle(col, i))}
2696
+ >
2697
+ <span class="header-cell__label">${col.label ?? col.id}</span>
2698
+ <div
2699
+ class="header-cell__resize"
2700
+ @mousedown=${(e) => this._handleResizeStart(e, col.id)}
2701
+ ></div>
2702
+ </div>
2703
+ `)}
2704
+ </div>
2705
+ ${this._data.map(row => b `
2706
+ <div class="row">
2707
+ ${this.getDisplayedColumns().map((col, i) => b `
2708
+ <div
2709
+ class=${e$1(this._getCellClasses(col, i))}
2710
+ style=${o$1(this._getCellStyle(col, i))}
2711
+ >
2712
+ ${this._renderCellContent(col, row)}
2713
+ </div>
2714
+ `)}
2715
+ </div>
2716
+ `)}
2717
+ </div>
2718
+ </div>
2719
+ </div>
2720
+ `;
2721
+ }
2722
+ /**
2723
+ * Renders a data table with:
2724
+ * - Header bar with title, search input with view selector, and tools (pagination, refresh, column visibility, actions dropdown)
2725
+ * - Scrollable grid with sticky header row and optional sticky left/right columns
2726
+ * - Loading, error message, or empty state when no data
2727
+ */
2728
+ render() {
2729
+ if (!this.def.columns.length) {
2730
+ return b `<slot></slot>`;
2731
+ }
2732
+ return b `
2733
+ ${this._renderHeader()}
2734
+ ${this._renderTable()}
2735
+ `;
2736
+ }
2737
+ };
2738
+ exports.KRTable.styles = [krBaseCSS, i$5 `
2739
+ /* -------------------------------------------------------------------------
2740
+ * Host
2741
+ * ----------------------------------------------------------------------- */
2742
+ :host {
2743
+ display: flex;
2744
+ flex-direction: column;
2745
+ width: 100%;
2746
+ height: 100%;
2747
+ overflow: hidden;
2748
+ container-type: inline-size;
2749
+ }
2750
+
2751
+ /* -------------------------------------------------------------------------
2752
+ * Header
2753
+ * ----------------------------------------------------------------------- */
2754
+ .header {
2755
+ flex-shrink: 0;
2756
+ display: flex;
2757
+ align-items: center;
2758
+ gap: 16px;
2759
+ margin: 0 24px;
2760
+ padding: 0 4px;
2761
+ height: 64px;
2762
+ border-bottom: 1px solid #e5e7eb;
2763
+ background: #fff;
2764
+ }
2765
+
2766
+ :host(.kr-table--scroll-edge) .header {
2767
+ border-bottom: none;
2768
+ }
2769
+
2770
+ .title {
2771
+ font-size: 20px;
2772
+ font-weight: 400;
2773
+ color: #000;
2774
+ }
2775
+
2776
+ /* -------------------------------------------------------------------------
2777
+ * Content
2778
+ * ----------------------------------------------------------------------- */
2779
+ .wrapper {
2780
+ flex: 1;
2781
+ position: relative;
2782
+ overflow: hidden;
2783
+ }
2784
+
2785
+ .content {
2786
+ height: 100%;
2787
+ overflow: auto;
2788
+ padding-bottom: 24px;
2789
+ }
2790
+
2791
+ /* -------------------------------------------------------------------------
2792
+ * Search
2793
+ * ----------------------------------------------------------------------- */
2794
+ .search {
2795
+ flex: 1;
2796
+ display: flex;
2797
+ align-items: center;
2798
+ justify-content: center;
2799
+ min-width: 0;
2800
+ }
2801
+
2802
+ .search-field {
2803
+ width: 100%;
2804
+ max-width: 400px;
2805
+ position: relative;
2806
+ display: flex;
2807
+ align-items: center;
2808
+ border: 1px solid #00000038;
2809
+ border-radius: 18px;
2810
+ transition: border-color 0.2s, box-shadow 0.2s;
2811
+ }
2812
+
2813
+ .search-field:focus-within {
2814
+ border-color: #163052;
2815
+ box-shadow: 0 0 0 3px rgba(22, 48, 82, 0.1);
2816
+ }
2817
+
2818
+ /* TODO: Uncomment when views dropdown is added
2819
+ .search-field:focus-within .views {
2820
+ border-color: #163052;
2821
+ }
2822
+ */
2823
+
2824
+ .search-icon {
2825
+ position: absolute;
2826
+ left: 16px;
2827
+ width: 20px;
2828
+ height: 20px;
2829
+ color: #656871;
2830
+ pointer-events: none;
2831
+ }
2832
+
2833
+ .search-input {
2834
+ height: 36px;
2835
+ padding: 0 16px 0 42px;
2836
+ border: none;
2837
+ border-radius: 16px;
2838
+ font-size: 14px;
2839
+ font-weight: 400;
2840
+ font-family: inherit;
2841
+ color: #163052;
2842
+ background: transparent;
2843
+ outline: none;
2844
+ flex: 1;
2845
+ min-width: 0;
2846
+ width: 100%;
2847
+ }
2848
+
2849
+ .search-input::placeholder {
2850
+ color: #656871;
2851
+ font-weight: 400;
2852
+ }
2853
+
2854
+ .search-input:focus {
2855
+ outline: none;
2856
+ }
2857
+
2858
+ @container (max-width: 800px) {
2859
+ .search-field {
2860
+ max-width: 250px;
2861
+ }
2862
+ }
2863
+
2864
+ .views {
2865
+ display: flex;
2866
+ align-items: center;
2867
+ gap: 4px;
2868
+ height: 36px;
2869
+ padding: 0 16px;
2870
+ border: 1px solid #00000038;
2871
+ border-right: none;
2872
+ border-radius: 16px 0 0 16px;
2873
+ font-size: 14px;
2874
+ font-family: inherit;
2875
+ color: #163052;
2876
+ background: transparent;
2877
+ cursor: pointer;
2878
+ white-space: nowrap;
2879
+ transition: border-color 0.2s;
2880
+ }
2881
+
2882
+ .views:hover {
2883
+ background: #e8f0f8;
2884
+ }
2885
+
2886
+ .views svg {
2887
+ width: 16px;
2888
+ height: 16px;
2889
+ color: #163052;
2890
+ }
2891
+
2892
+ /* -------------------------------------------------------------------------
2893
+ * Pagination
2894
+ * ----------------------------------------------------------------------- */
2895
+ .tools {
2896
+ display: flex;
2897
+ align-items: center;
2898
+ gap: 8px;
2899
+ }
2900
+
2901
+ .pagination {
2902
+ display: flex;
2903
+ align-items: center;
2904
+ gap: 2px;
2905
+ }
2906
+
2907
+ .pagination-info {
2908
+ font-size: 13px;
2909
+ color: var(--kr-primary);
2910
+ white-space: nowrap;
2911
+ }
2912
+
2913
+ .pagination-icon {
2914
+ display: flex;
2915
+ color: var(--kr-primary);
2916
+ cursor: pointer;
2917
+ }
2918
+
2919
+ .pagination-icon--disabled {
2920
+ opacity: 0.3;
2921
+ pointer-events: none;
2922
+ }
2923
+
2924
+ .pagination-icon svg {
2925
+ width: 24px;
2926
+ height: 24px;
2927
+ }
2928
+
2929
+ /* -------------------------------------------------------------------------
2930
+ * Header Icons
2931
+ * ----------------------------------------------------------------------- */
2932
+ .refresh,
2933
+ .header-icon {
2934
+ display: flex;
2935
+ align-items: center;
2936
+ justify-content: center;
2937
+ color: var(--kr-primary);
2938
+ background: #EBF1FA;
2939
+ cursor: pointer;
2940
+ padding: 6px;
2941
+ border-radius: 50%;
2942
+ transition: background 0.15s;
2943
+ }
2944
+
2945
+ .refresh:hover,
2946
+ .header-icon:hover {
2947
+ background: #e8f0f8;
2948
+ }
2949
+
2950
+ .refresh svg,
2951
+ .header-icon svg {
2952
+ width: 24px;
2953
+ height: 24px;
2954
+ }
2955
+
2956
+ /* -------------------------------------------------------------------------
2957
+ * Column Picker
2958
+ * ----------------------------------------------------------------------- */
2959
+ .column-picker-wrapper {
2960
+ position: relative;
2961
+ }
2962
+
2963
+ .column-picker {
2964
+ position: absolute;
2965
+ top: 100%;
2966
+ right: 0;
2967
+ margin-top: 4px;
2968
+ min-width: 200px;
2969
+ max-height: calc(100vh - 120px);
2970
+ overflow-y: auto;
2971
+ background: white;
2972
+ border: 1px solid #9ba7b6;
2973
+ border-radius: 8px;
2974
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
2975
+ padding: 8px 0;
2976
+ z-index: 100;
2977
+ display: none;
2978
+ transform-origin: top;
2979
+ }
2980
+
2981
+ .column-picker.open {
2982
+ display: block;
2983
+ animation: column-picker-fade-in 150ms ease-out;
2984
+ }
2985
+
2986
+ @keyframes column-picker-fade-in {
2987
+ from {
2988
+ opacity: 0;
2989
+ transform: translateY(-4px);
2990
+ }
2991
+ to {
2992
+ opacity: 1;
2993
+ transform: translateY(0);
2994
+ }
2995
+ }
2996
+
2997
+ .column-picker-item {
2998
+ display: flex;
2999
+ align-items: center;
3000
+ gap: 10px;
3001
+ padding: 8px 16px;
3002
+ cursor: pointer;
3003
+ white-space: nowrap;
3004
+ }
3005
+
3006
+ .column-picker-item:hover {
3007
+ background: #f3f4f6;
3008
+ }
3009
+
3010
+ .column-picker-checkbox {
3011
+ width: 16px;
3012
+ height: 16px;
3013
+ border: 1.5px solid #9ca3af;
3014
+ border-radius: 3px;
3015
+ display: flex;
3016
+ align-items: center;
3017
+ justify-content: center;
3018
+ flex-shrink: 0;
3019
+ transition: all 0.15s;
3020
+ }
3021
+
3022
+ .column-picker-checkbox.checked {
3023
+ background: var(--kr-primary);
3024
+ border-color: var(--kr-primary);
3025
+ }
3026
+
3027
+ .column-picker-checkbox svg {
3028
+ width: 12px;
3029
+ height: 12px;
3030
+ color: white;
3031
+ opacity: 0;
3032
+ }
3033
+
3034
+ .column-picker-checkbox.checked svg {
3035
+ opacity: 1;
3036
+ }
3037
+
3038
+ .column-picker-label {
3039
+ font-size: 14px;
3040
+ color: #374151;
3041
+ }
3042
+
3043
+ /* -------------------------------------------------------------------------
3044
+ * Table Structure
3045
+ * ----------------------------------------------------------------------- */
3046
+ .table {
3047
+ display: flex;
3048
+ flex-direction: column;
3049
+ width: 100%;
3050
+ min-width: max-content;
3051
+ font-size: 14px;
3052
+ }
3053
+
3054
+ .row {
3055
+ display: flex;
3056
+ }
3057
+
3058
+ .header-row {
3059
+ display: flex;
3060
+ position: sticky;
3061
+ top: 0;
3062
+ z-index: 2;
3063
+ }
3064
+
3065
+ .row:hover .cell {
3066
+ background: #f9fafb;
3067
+ }
3068
+
3069
+ .cell {
3070
+ padding: 12px 16px;
3071
+ white-space: nowrap;
3072
+ overflow: hidden;
3073
+ text-overflow: ellipsis;
3074
+ flex-shrink: 0;
3075
+ box-sizing: border-box;
3076
+ }
3077
+
3078
+ .header-cell {
3079
+ display: flex;
3080
+ align-items: center;
3081
+ padding: 12px 16px;
3082
+ white-space: nowrap;
3083
+ flex-shrink: 0;
3084
+ box-sizing: border-box;
3085
+ background: #f9fafb;
3086
+ border-bottom: 2px solid #e5e7eb;
3087
+ font-weight: 600;
3088
+ color: #374151;
3089
+ overflow: visible;
3090
+ }
3091
+
3092
+ .header-cell__label {
3093
+ flex: 1;
3094
+ overflow: hidden;
3095
+ text-overflow: ellipsis;
3096
+ }
3097
+
3098
+ .header-cell__resize {
3099
+ width: 14px;
3100
+ margin-right: -22px;
3101
+ align-self: stretch;
3102
+ cursor: col-resize;
3103
+ display: flex;
3104
+ align-items: center;
3105
+ justify-content: center;
3106
+ z-index: 1;
3107
+ }
3108
+
3109
+ .header-cell__resize::after {
3110
+ content: '';
3111
+ width: 2px;
3112
+ height: 20px;
3113
+ background: #c6c6cd;
3114
+ }
3115
+
3116
+ .header-cell:last-child .header-cell__resize::after {
3117
+ display: none;
3118
+ }
3119
+
3120
+ .cell {
3121
+ background: #fff;
3122
+ border-bottom: 1px solid #e5e7eb;
3123
+ color: #1f2937;
3124
+ }
3125
+
3126
+ .cell--align-center {
3127
+ text-align: center;
3128
+ }
3129
+
3130
+ .cell--align-right {
3131
+ text-align: right;
3132
+ }
3133
+
3134
+ .cell--sticky-left,
3135
+ .cell--sticky-right {
3136
+ position: sticky;
3137
+ z-index: 1;
3138
+ }
3139
+
3140
+ .header-cell--sticky-left,
3141
+ .header-cell--sticky-right {
3142
+ position: sticky;
3143
+ z-index: 3;
3144
+ }
3145
+
3146
+ .header-cell--align-center {
3147
+ text-align: center;
3148
+ }
3149
+
3150
+ .header-cell--align-right {
3151
+ text-align: right;
3152
+ }
3153
+
3154
+ .header-cell--sticky-left-last,
3155
+ .cell--sticky-left-last {
3156
+ border-right: 1px solid #d1d5db;
3157
+ }
3158
+
3159
+ .header-cell--sticky-right-first,
3160
+ .cell--sticky-right-first {
3161
+ border-left: 1px solid #d1d5db;
3162
+ }
3163
+
3164
+ /* -------------------------------------------------------------------------
3165
+ * Scroll Mode: Edge
3166
+ * Padding scrolls with content, table can reach edges when scrolling
3167
+ * ----------------------------------------------------------------------- */
3168
+ :host(.kr-table--scroll-edge) .table {
3169
+ padding-left: 24px;
3170
+ }
3171
+
3172
+ /* Only add right padding when no horizontal scroll is needed */
3173
+ :host(.kr-table--scroll-edge):not(.kr-table--scroll-horizontal-available) .table {
3174
+ padding-right: 24px;
3175
+ }
3176
+
3177
+ :host(.kr-table--scroll-edge) .header-row .header-cell {
3178
+ border-top: 1px solid #e5e7eb;
3179
+ }
3180
+
3181
+ /* -------------------------------------------------------------------------
3182
+ * Scroll Mode: Overlay
3183
+ * Fixed padding with overlay elements that hide content at edges
3184
+ * ----------------------------------------------------------------------- */
3185
+ :host(.kr-table--scroll-overlay) .content {
3186
+ padding-left: 24px;
3187
+ padding-right: 24px;
3188
+ }
3189
+
3190
+ .overlay-left,
3191
+ .overlay-right {
3192
+ display: none;
3193
+ position: absolute;
3194
+ top: 0;
3195
+ bottom: 0;
3196
+ width: 24px;
3197
+ z-index: 5;
3198
+ pointer-events: none;
3199
+ transition: box-shadow 0.15s ease;
3200
+ }
3201
+
3202
+ :host(.kr-table--scroll-overlay) .overlay-left,
3203
+ :host(.kr-table--scroll-overlay) .overlay-right {
3204
+ display: block;
3205
+ }
3206
+
3207
+ .overlay-left {
3208
+ left: 0;
3209
+ background: linear-gradient(to right, #fff 50%, transparent);
3210
+ }
3211
+
3212
+ .overlay-right {
3213
+ right: 0;
3214
+ background: linear-gradient(to left, #fff 50%, transparent);
3215
+ }
3216
+
3217
+ :host(.kr-table--scroll-left-available) .overlay-left {
3218
+ box-shadow: inset -6px 0 6px -6px rgba(0, 0, 0, 0.08);
3219
+ }
3220
+
3221
+ :host(.kr-table--scroll-right-available) .overlay-right {
3222
+ box-shadow: inset 6px 0 6px -6px rgba(0, 0, 0, 0.08);
3223
+ }
3224
+
3225
+ /* -------------------------------------------------------------------------
3226
+ * Status (Loading, Error, Empty)
3227
+ * ----------------------------------------------------------------------- */
3228
+ .status {
3229
+ position: absolute;
3230
+ top: 0;
3231
+ left: 0;
3232
+ right: 0;
3233
+ bottom: 0;
3234
+ display: flex;
3235
+ align-items: center;
3236
+ justify-content: center;
3237
+ font-size: 14px;
3238
+ font-weight: 400;
3239
+ color: #5f6368;
3240
+ pointer-events: none;
3241
+ }
3242
+
3243
+ .status--error {
3244
+ color: #dc2626;
3245
+ }
3246
+ `];
3247
+ __decorate$3([
3248
+ r$1()
3249
+ ], exports.KRTable.prototype, "_data", void 0);
3250
+ __decorate$3([
3251
+ r$1()
3252
+ ], exports.KRTable.prototype, "_dataState", void 0);
3253
+ __decorate$3([
3254
+ r$1()
3255
+ ], exports.KRTable.prototype, "_page", void 0);
3256
+ __decorate$3([
3257
+ r$1()
3258
+ ], exports.KRTable.prototype, "_pageSize", void 0);
3259
+ __decorate$3([
3260
+ r$1()
3261
+ ], exports.KRTable.prototype, "_totalItems", void 0);
3262
+ __decorate$3([
3263
+ r$1()
3264
+ ], exports.KRTable.prototype, "_totalPages", void 0);
3265
+ __decorate$3([
3266
+ r$1()
3267
+ ], exports.KRTable.prototype, "_searchQuery", void 0);
3268
+ __decorate$3([
3269
+ r$1()
3270
+ ], exports.KRTable.prototype, "_canScrollLeft", void 0);
3271
+ __decorate$3([
3272
+ r$1()
3273
+ ], exports.KRTable.prototype, "_canScrollRight", void 0);
3274
+ __decorate$3([
3275
+ r$1()
3276
+ ], exports.KRTable.prototype, "_canScrollHorizontal", void 0);
3277
+ __decorate$3([
3278
+ r$1()
3279
+ ], exports.KRTable.prototype, "_columnPickerOpen", void 0);
3280
+ __decorate$3([
3281
+ r$1()
3282
+ ], exports.KRTable.prototype, "_displayedColumns", void 0);
3283
+ __decorate$3([
3284
+ r$1()
3285
+ ], exports.KRTable.prototype, "_columnWidths", void 0);
3286
+ __decorate$3([
3287
+ n$1({ type: Object })
3288
+ ], exports.KRTable.prototype, "def", void 0);
3289
+ exports.KRTable = __decorate$3([
3290
+ t$1('kr-table')
3291
+ ], exports.KRTable);
3292
+
2038
3293
  /**
2039
3294
  * @license
2040
3295
  * Copyright 2018 Google LLC
@@ -2272,8 +3527,8 @@
2272
3527
 
2273
3528
  input:focus {
2274
3529
  outline: none;
2275
- border-color: var(--kr-text-field-focus-border-color, #2563eb);
2276
- box-shadow: 0 0 0 3px var(--kr-text-field-focus-ring-color, rgba(37, 99, 235, 0.1));
3530
+ border-color: var(--kr-text-field-focus-border-color, #163052);
3531
+ box-shadow: 0 0 0 3px var(--kr-text-field-focus-ring-color, rgba(22, 48, 82, 0.1));
2277
3532
  }
2278
3533
 
2279
3534
  input:disabled {