@design.estate/dees-catalog 3.61.2 → 3.63.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.
@@ -163645,6 +163645,44 @@ var demoFunc29 = /* @__PURE__ */ __name(() => b2`
163645
163645
  ></dees-table>
163646
163646
  </div>
163647
163647
 
163648
+ <div class="demo-section">
163649
+ <h2 class="demo-title">Multi-Column Sort</h2>
163650
+ <p class="demo-description">
163651
+ Click any column header for a single-column sort. Hold Shift while clicking to add the
163652
+ column to a multi-sort cascade (or cycle its direction). Right-click any sortable header
163653
+ to open a menu where you can pin a column to a specific priority slot, remove it, or
163654
+ clear the cascade.
163655
+ </p>
163656
+ <dees-table
163657
+ heading1="People Directory"
163658
+ heading2="Pre-seeded with department ▲ 1, name ▲ 2"
163659
+ .sortBy=${[
163660
+ { key: "department", dir: "asc" },
163661
+ { key: "name", dir: "asc" }
163662
+ ]}
163663
+ .columns=${[
163664
+ { key: "department", header: "Department", sortable: true },
163665
+ { key: "name", header: "Name", sortable: true },
163666
+ { key: "role", header: "Role", sortable: true },
163667
+ { key: "createdAt", header: "Created", sortable: true },
163668
+ { key: "location", header: "Location", sortable: true },
163669
+ { key: "status", header: "Status", sortable: true }
163670
+ ]}
163671
+ .data=${[
163672
+ { department: "R&D", name: "Alice Johnson", role: "Engineer", createdAt: "2023-01-12", location: "Berlin", status: "Active" },
163673
+ { department: "R&D", name: "Diana Martinez", role: "Engineer", createdAt: "2020-06-30", location: "Madrid", status: "Active" },
163674
+ { department: "R&D", name: "Mark Lee", role: "Engineer", createdAt: "2024-03-04", location: "Berlin", status: "Active" },
163675
+ { department: "Design", name: "Bob Smith", role: "Designer", createdAt: "2022-11-05", location: "Paris", status: "Active" },
163676
+ { department: "Design", name: "Sara Kim", role: "Designer", createdAt: "2021-08-19", location: "Paris", status: "On Leave" },
163677
+ { department: "Ops", name: "Charlie Davis", role: "Manager", createdAt: "2021-04-21", location: "London", status: "On Leave" },
163678
+ { department: "Ops", name: "Helena Voss", role: "SRE", createdAt: "2023-07-22", location: "London", status: "Active" },
163679
+ { department: "QA", name: "Fiona Clark", role: "QA", createdAt: "2022-03-14", location: "Vienna", status: "Active" },
163680
+ { department: "QA", name: "Tom\xE1s Rivera", role: "QA", createdAt: "2024-01-09", location: "Madrid", status: "Active" },
163681
+ { department: "CS", name: "Ethan Brown", role: "Support", createdAt: "2019-09-18", location: "Rome", status: "Inactive" }
163682
+ ]}
163683
+ ></dees-table>
163684
+ </div>
163685
+
163648
163686
  <div class="demo-section">
163649
163687
  <h2 class="demo-title">Wide Properties + Many Actions</h2>
163650
163688
  <p class="demo-description">A table with many columns and rich actions to stress test layout and sticky Actions.</p>
@@ -163876,29 +163914,48 @@ var tableStyles = [
163876
163914
  border-bottom-width: 0px;
163877
163915
  }
163878
163916
 
163917
+ /* Default mode (Mode B, page sticky): horizontal scroll lives on
163918
+ .tableScroll (so wide tables don't get clipped by an ancestor
163919
+ overflow:hidden such as dees-tile). Vertical sticky is handled by
163920
+ a JS-managed floating header (.floatingHeader, position:fixed),
163921
+ which is unaffected by ancestor overflow. */
163879
163922
  .tableScroll {
163880
- /* enable horizontal scroll only when content exceeds width */
163923
+ position: relative;
163881
163924
  overflow-x: auto;
163882
- /* prevent vertical scroll inside the table container */
163883
- overflow-y: hidden;
163884
- /* avoid reserving extra space for classic scrollbars where possible */
163925
+ overflow-y: visible;
163926
+ scrollbar-gutter: stable;
163927
+ }
163928
+ /* Mode A, internal scroll: opt-in via fixed-height attribute.
163929
+ The table scrolls inside its own box and the header sticks via plain CSS sticky. */
163930
+ :host([fixed-height]) .tableScroll {
163931
+ max-height: var(--table-max-height, 360px);
163932
+ overflow: auto;
163885
163933
  scrollbar-gutter: stable both-edges;
163886
163934
  }
163887
- /* Hide horizontal scrollbar entirely when not using sticky header */
163888
- :host(:not([sticky-header])) .tableScroll {
163889
- -ms-overflow-style: none; /* IE/Edge */
163890
- scrollbar-width: none; /* Firefox (hides both axes) */
163935
+ :host([fixed-height]) .tableScroll::-webkit-scrollbar:horizontal {
163936
+ height: 0px;
163937
+ }
163938
+
163939
+ /* Floating header overlay (Mode B). Position is managed by JS so it
163940
+ escapes any ancestor overflow:hidden (position:fixed is not clipped
163941
+ by overflow ancestors). */
163942
+ .floatingHeader {
163943
+ position: fixed;
163944
+ top: 0;
163945
+ left: 0;
163946
+ z-index: 100;
163947
+ visibility: hidden;
163948
+ overflow: hidden;
163949
+ pointer-events: none;
163891
163950
  }
163892
- :host(:not([sticky-header])) .tableScroll::-webkit-scrollbar {
163893
- display: none; /* Chrome/Safari */
163951
+ .floatingHeader.active {
163952
+ visibility: visible;
163894
163953
  }
163895
- /* In sticky-header mode, hide only the horizontal scrollbar in WebKit/Blink */
163896
- :host([sticky-header]) .tableScroll::-webkit-scrollbar:horizontal {
163897
- height: 0px;
163954
+ .floatingHeader table {
163955
+ margin: 0;
163898
163956
  }
163899
- :host([sticky-header]) .tableScroll {
163900
- max-height: var(--table-max-height, 360px);
163901
- overflow: auto;
163957
+ .floatingHeader th {
163958
+ pointer-events: auto;
163902
163959
  }
163903
163960
 
163904
163961
  table {
@@ -163921,11 +163978,20 @@ var tableStyles = [
163921
163978
  background: ${cssManager.bdTheme("hsl(210 40% 96.1%)", "hsl(0 0% 9%)")};
163922
163979
  border-bottom: 1px solid var(--dees-color-border-strong);
163923
163980
  }
163924
- :host([sticky-header]) thead th {
163981
+ /* th needs its own background so sticky cells paint over scrolled rows
163982
+ (browsers don't paint the <thead> box behind a sticky <th>). */
163983
+ th {
163984
+ background: ${cssManager.bdTheme("hsl(210 40% 96.1%)", "hsl(0 0% 9%)")};
163985
+ }
163986
+ /* Mode A — internal scroll sticky */
163987
+ :host([fixed-height]) thead th {
163925
163988
  position: sticky;
163926
163989
  top: 0;
163927
163990
  z-index: 2;
163928
163991
  }
163992
+ :host([fixed-height]) thead tr.filtersRow th {
163993
+ top: 36px; /* matches th { height: 36px } below */
163994
+ }
163929
163995
 
163930
163996
  tbody tr {
163931
163997
  transition: background-color 0.15s ease;
@@ -164038,6 +164104,32 @@ var tableStyles = [
164038
164104
  color: ${cssManager.bdTheme("hsl(215.4 16.3% 46.9%)", "hsl(215 20.2% 65.1%)")};
164039
164105
  letter-spacing: -0.01em;
164040
164106
  }
164107
+
164108
+ th[role='columnheader']:hover {
164109
+ color: var(--dees-color-text-primary);
164110
+ }
164111
+
164112
+ th .sortArrow {
164113
+ display: inline-block;
164114
+ margin-left: 6px;
164115
+ font-size: 10px;
164116
+ line-height: 1;
164117
+ opacity: 0.7;
164118
+ vertical-align: middle;
164119
+ }
164120
+
164121
+ th .sortBadge {
164122
+ display: inline-block;
164123
+ margin-left: 3px;
164124
+ padding: 1px 5px;
164125
+ font-size: 10px;
164126
+ font-weight: 600;
164127
+ line-height: 1;
164128
+ color: ${cssManager.bdTheme("hsl(222.2 47.4% 30%)", "hsl(217.2 91.2% 75%)")};
164129
+ background: ${cssManager.bdTheme("hsl(222.2 47.4% 51.2% / 0.12)", "hsl(217.2 91.2% 59.8% / 0.18)")};
164130
+ border-radius: 999px;
164131
+ vertical-align: middle;
164132
+ }
164041
164133
 
164042
164134
  :host([show-vertical-lines]) th {
164043
164135
  border-right: 1px solid var(--dees-color-border-default);
@@ -164229,7 +164321,23 @@ function getCellValue(row, col, displayFunction) {
164229
164321
  return col.value ? col.value(row) : row[col.key];
164230
164322
  }
164231
164323
  __name(getCellValue, "getCellValue");
164232
- function getViewData(data, effectiveColumns, sortKey, sortDir, filterText, columnFilters, filterMode = "table", lucenePredicate) {
164324
+ function compareCellValues(va, vb) {
164325
+ if (va == null && vb == null) return 0;
164326
+ if (va == null) return -1;
164327
+ if (vb == null) return 1;
164328
+ if (typeof va === "number" && typeof vb === "number") {
164329
+ if (va < vb) return -1;
164330
+ if (va > vb) return 1;
164331
+ return 0;
164332
+ }
164333
+ const sa = String(va).toLowerCase();
164334
+ const sb = String(vb).toLowerCase();
164335
+ if (sa < sb) return -1;
164336
+ if (sa > sb) return 1;
164337
+ return 0;
164338
+ }
164339
+ __name(compareCellValues, "compareCellValues");
164340
+ function getViewData(data, effectiveColumns, sortBy, filterText, columnFilters, filterMode = "table", lucenePredicate) {
164233
164341
  let arr = data.slice();
164234
164342
  const ft = (filterText || "").trim().toLowerCase();
164235
164343
  const cf = columnFilters || {};
@@ -164243,9 +164351,9 @@ function getViewData(data, effectiveColumns, sortKey, sortDir, filterText, colum
164243
164351
  const needle = String(cf[k4]).toLowerCase();
164244
164352
  if (!s10.includes(needle)) return false;
164245
164353
  } else {
164246
- const col2 = effectiveColumns.find((c11) => String(c11.key) === k4);
164247
- if (!col2 || col2.hidden || col2.filterable === false) continue;
164248
- const val = getCellValue(row, col2);
164354
+ const col = effectiveColumns.find((c11) => String(c11.key) === k4);
164355
+ if (!col || col.hidden || col.filterable === false) continue;
164356
+ const val = getCellValue(row, col);
164249
164357
  const s10 = String(val ?? "").toLowerCase();
164250
164358
  const needle = String(cf[k4]).toLowerCase();
164251
164359
  if (!s10.includes(needle)) return false;
@@ -164266,9 +164374,9 @@ function getViewData(data, effectiveColumns, sortKey, sortDir, filterText, colum
164266
164374
  }
164267
164375
  }
164268
164376
  } else {
164269
- for (const col2 of effectiveColumns) {
164270
- if (col2.hidden) continue;
164271
- const val = getCellValue(row, col2);
164377
+ for (const col of effectiveColumns) {
164378
+ if (col.hidden) continue;
164379
+ const val = getCellValue(row, col);
164272
164380
  const s10 = String(val ?? "").toLowerCase();
164273
164381
  if (s10.includes(ft)) {
164274
164382
  any = true;
@@ -164281,21 +164389,14 @@ function getViewData(data, effectiveColumns, sortKey, sortDir, filterText, colum
164281
164389
  return true;
164282
164390
  });
164283
164391
  }
164284
- if (!sortKey || !sortDir) return arr;
164285
- const col = effectiveColumns.find((c11) => String(c11.key) === sortKey);
164286
- if (!col) return arr;
164287
- const dir = sortDir === "asc" ? 1 : -1;
164392
+ if (!sortBy || sortBy.length === 0) return arr;
164393
+ const resolved2 = sortBy.map((desc) => ({ desc, col: effectiveColumns.find((c11) => String(c11.key) === desc.key) })).filter((entry) => !!entry.col);
164394
+ if (resolved2.length === 0) return arr;
164288
164395
  arr.sort((a5, b5) => {
164289
- const va = getCellValue(a5, col);
164290
- const vb = getCellValue(b5, col);
164291
- if (va == null && vb == null) return 0;
164292
- if (va == null) return -1 * dir;
164293
- if (vb == null) return 1 * dir;
164294
- if (typeof va === "number" && typeof vb === "number") return (va - vb) * dir;
164295
- const sa = String(va).toLowerCase();
164296
- const sb = String(vb).toLowerCase();
164297
- if (sa < sb) return -1 * dir;
164298
- if (sa > sb) return 1 * dir;
164396
+ for (const { desc, col } of resolved2) {
164397
+ const cmp2 = compareCellValues(getCellValue(a5, col), getCellValue(b5, col));
164398
+ if (cmp2 !== 0) return desc.dir === "asc" ? cmp2 : -cmp2;
164399
+ }
164299
164400
  return 0;
164300
164401
  });
164301
164402
  return arr;
@@ -164448,7 +164549,13 @@ __name(compileLucenePredicate, "compileLucenePredicate");
164448
164549
  init_dist_ts30();
164449
164550
  init_dist_ts29();
164450
164551
  init_theme();
164451
- var _selectedIds_dec, _selectionMode_dec, _searchMode_dec, _stickyHeader_dec, _showColumnFilters_dec, _columnFilters_dec, _filterText_dec, _sortDir_dec, _sortKey_dec, _showGrid_dec, _showHorizontalLines_dec, _showVerticalLines_dec, _editableFields_dec, _selectedDataRow_dec, _reverseDisplayFunction_dec, _displayFunction_dec, _augmentFromDisplayFunction_dec, _rowKey_dec, _columns_dec, _dataActions_dec, _searchable_dec, _dataName_dec, _required_dec3, _disabled_dec3, _label_dec3, _key_dec2, _data_dec, _heading2_dec, _heading1_dec, _a42, _DeesTable_decorators, _init39, _heading1, _heading22, _data, _key2, _label3, _disabled3, _required3, _dataName, _searchable, _dataActions, _columns, _rowKey, _augmentFromDisplayFunction, _displayFunction, _reverseDisplayFunction, _selectedDataRow, _editableFields, _showVerticalLines, _showHorizontalLines, _showGrid, _sortKey, _sortDir, _filterText, _columnFilters, _showColumnFilters, _stickyHeader, _searchMode, _selectionMode, _selectedIds;
164552
+ var _selectedIds_dec, _selectionMode_dec, _searchMode_dec, _fixedHeight_dec, _showColumnFilters_dec, _columnFilters_dec, _filterText_dec, _sortBy_dec, _showGrid_dec, _showHorizontalLines_dec, _showVerticalLines_dec, _editableFields_dec, _selectedDataRow_dec, _reverseDisplayFunction_dec, _displayFunction_dec, _augmentFromDisplayFunction_dec, _rowKey_dec, _columns_dec, _dataActions_dec, _searchable_dec, _dataName_dec, _required_dec3, _disabled_dec3, _label_dec3, _key_dec2, _data_dec, _heading2_dec, _heading1_dec, _a42, _DeesTable_decorators, _init39, _heading1, _heading22, _data, _key2, _label3, _disabled3, _required3, _dataName, _searchable, _dataActions, _columns, _rowKey, _augmentFromDisplayFunction, _displayFunction, _reverseDisplayFunction, _selectedDataRow, _editableFields, _showVerticalLines, _showHorizontalLines, _showGrid, _sortBy, _filterText, _columnFilters, _showColumnFilters, _fixedHeight, _searchMode, _selectionMode, _selectedIds;
164553
+ function ordinalLabel(n12) {
164554
+ const s10 = ["th", "st", "nd", "rd"];
164555
+ const v5 = n12 % 100;
164556
+ return n12 + (s10[(v5 - 20) % 10] || s10[v5] || s10[0]);
164557
+ }
164558
+ __name(ordinalLabel, "ordinalLabel");
164452
164559
  _DeesTable_decorators = [customElement("dees-table")];
164453
164560
  var _DeesTable = class _DeesTable extends (_a42 = DeesElement, _heading1_dec = [n5({
164454
164561
  type: String
@@ -164491,7 +164598,7 @@ var _DeesTable = class _DeesTable extends (_a42 = DeesElement, _heading1_dec = [
164491
164598
  type: Boolean,
164492
164599
  reflect: true,
164493
164600
  attribute: "show-grid"
164494
- })], _sortKey_dec = [n5({ attribute: false })], _sortDir_dec = [n5({ attribute: false })], _filterText_dec = [n5({ type: String })], _columnFilters_dec = [n5({ attribute: false })], _showColumnFilters_dec = [n5({ type: Boolean, attribute: "show-column-filters" })], _stickyHeader_dec = [n5({ type: Boolean, reflect: true, attribute: "sticky-header" })], _searchMode_dec = [n5({ type: String })], _selectionMode_dec = [n5({ type: String })], _selectedIds_dec = [n5({ attribute: false })], _a42) {
164601
+ })], _sortBy_dec = [n5({ attribute: false })], _filterText_dec = [n5({ type: String })], _columnFilters_dec = [n5({ attribute: false })], _showColumnFilters_dec = [n5({ type: Boolean, attribute: "show-column-filters" })], _fixedHeight_dec = [n5({ type: Boolean, reflect: true, attribute: "fixed-height" })], _searchMode_dec = [n5({ type: String })], _selectionMode_dec = [n5({ type: String })], _selectedIds_dec = [n5({ attribute: false })], _a42) {
164495
164602
  constructor() {
164496
164603
  super();
164497
164604
  __privateAdd(this, _heading1, __runInitializers(_init39, 8, this, "heading 1")), __runInitializers(_init39, 11, this);
@@ -164518,19 +164625,23 @@ var _DeesTable = class _DeesTable extends (_a42 = DeesElement, _heading1_dec = [
164518
164625
  __publicField(this, "files", []);
164519
164626
  __publicField(this, "fileWeakMap", /* @__PURE__ */ new WeakMap());
164520
164627
  __publicField(this, "dataChangeSubject", new domtools_pluginexports_exports.smartrx.rxjs.Subject());
164521
- __privateAdd(this, _sortKey, __runInitializers(_init39, 88, this)), __runInitializers(_init39, 91, this);
164522
- __privateAdd(this, _sortDir, __runInitializers(_init39, 92, this, null)), __runInitializers(_init39, 95, this);
164523
- __privateAdd(this, _filterText, __runInitializers(_init39, 96, this, "")), __runInitializers(_init39, 99, this);
164524
- __privateAdd(this, _columnFilters, __runInitializers(_init39, 100, this, {})), __runInitializers(_init39, 103, this);
164525
- __privateAdd(this, _showColumnFilters, __runInitializers(_init39, 104, this, false)), __runInitializers(_init39, 107, this);
164526
- __privateAdd(this, _stickyHeader, __runInitializers(_init39, 108, this, false)), __runInitializers(_init39, 111, this);
164527
- __privateAdd(this, _searchMode, __runInitializers(_init39, 112, this, "table")), __runInitializers(_init39, 115, this);
164628
+ __privateAdd(this, _sortBy, __runInitializers(_init39, 88, this, [])), __runInitializers(_init39, 91, this);
164629
+ __privateAdd(this, _filterText, __runInitializers(_init39, 92, this, "")), __runInitializers(_init39, 95, this);
164630
+ __privateAdd(this, _columnFilters, __runInitializers(_init39, 96, this, {})), __runInitializers(_init39, 99, this);
164631
+ __privateAdd(this, _showColumnFilters, __runInitializers(_init39, 100, this, false)), __runInitializers(_init39, 103, this);
164632
+ __privateAdd(this, _fixedHeight, __runInitializers(_init39, 104, this, false)), __runInitializers(_init39, 107, this);
164633
+ __privateAdd(this, _searchMode, __runInitializers(_init39, 108, this, "table")), __runInitializers(_init39, 111, this);
164528
164634
  __publicField(this, "__searchTextSub");
164529
164635
  __publicField(this, "__searchModeSub");
164530
- __privateAdd(this, _selectionMode, __runInitializers(_init39, 116, this, "none")), __runInitializers(_init39, 119, this);
164531
- __privateAdd(this, _selectedIds, __runInitializers(_init39, 120, this, /* @__PURE__ */ new Set())), __runInitializers(_init39, 123, this);
164636
+ __privateAdd(this, _selectionMode, __runInitializers(_init39, 112, this, "none")), __runInitializers(_init39, 115, this);
164637
+ __privateAdd(this, _selectedIds, __runInitializers(_init39, 116, this, /* @__PURE__ */ new Set())), __runInitializers(_init39, 119, this);
164532
164638
  __publicField(this, "_rowIdMap", /* @__PURE__ */ new WeakMap());
164533
164639
  __publicField(this, "_rowIdCounter", 0);
164640
+ // ─── Floating header (page-sticky) lifecycle ─────────────────────────
164641
+ __publicField(this, "__floatingResizeObserver");
164642
+ __publicField(this, "__floatingScrollHandler");
164643
+ __publicField(this, "__floatingActive", false);
164644
+ __publicField(this, "__scrollAncestors", []);
164534
164645
  __publicField(this, "__debounceTimer");
164535
164646
  }
164536
164647
  get value() {
@@ -164549,8 +164660,7 @@ var _DeesTable = class _DeesTable extends (_a42 = DeesElement, _heading1_dec = [
164549
164660
  const viewData = getViewData(
164550
164661
  this.data,
164551
164662
  effectiveColumns,
164552
- this.sortKey,
164553
- this.sortDir,
164663
+ this.sortBy,
164554
164664
  this.filterText,
164555
164665
  this.columnFilters,
164556
164666
  this.searchMode === "data" ? "data" : "table",
@@ -164622,57 +164732,7 @@ var _DeesTable = class _DeesTable extends (_a42 = DeesElement, _heading1_dec = [
164622
164732
  <div class="tableScroll">
164623
164733
  <table>
164624
164734
  <thead>
164625
- <tr>
164626
- ${this.selectionMode !== "none" ? b2`
164627
- <th style="width:42px; text-align:center;">
164628
- ${this.selectionMode === "multi" ? b2`
164629
- <dees-input-checkbox
164630
- .value=${this.areAllVisibleSelected()}
164631
- .indeterminate=${this.isVisibleSelectionIndeterminate()}
164632
- @newValue=${(e11) => {
164633
- e11.stopPropagation();
164634
- this.setSelectVisible(e11.detail === true);
164635
- }}
164636
- ></dees-input-checkbox>
164637
- ` : b2``}
164638
- </th>
164639
- ` : b2``}
164640
- ${effectiveColumns.filter((c11) => !c11.hidden).map((col) => {
164641
- const isSortable = !!col.sortable;
164642
- const ariaSort = this.getAriaSort(col);
164643
- return b2`
164644
- <th
164645
- role="columnheader"
164646
- aria-sort=${ariaSort}
164647
- style="${isSortable ? "cursor: pointer;" : ""}"
164648
- @click=${() => isSortable ? this.toggleSort(col) : null}
164649
- >
164650
- ${col.header ?? col.key}
164651
- ${this.renderSortIndicator(col)}
164652
- </th>`;
164653
- })}
164654
- ${(() => {
164655
- if (this.dataActions && this.dataActions.length > 0) {
164656
- return b2` <th class="actionsCol">Actions</th> `;
164657
- }
164658
- })()}
164659
- </tr>
164660
- ${this.showColumnFilters ? b2`<tr class="filtersRow">
164661
- ${this.selectionMode !== "none" ? b2`<th style="width:42px;"></th>` : b2``}
164662
- ${effectiveColumns.filter((c11) => !c11.hidden).map((col) => {
164663
- const key2 = String(col.key);
164664
- if (col.filterable === false) return b2`<th></th>`;
164665
- return b2`<th>
164666
- <input type="text" placeholder="Filter..." .value=${this.columnFilters[key2] || ""}
164667
- @input=${(e11) => this.setColumnFilter(key2, e11.target.value)} />
164668
- </th>`;
164669
- })}
164670
- ${(() => {
164671
- if (this.dataActions && this.dataActions.length > 0) {
164672
- return b2` <th></th> `;
164673
- }
164674
- })()}
164675
- </tr>` : b2``}
164735
+ ${this.renderHeaderRows(effectiveColumns)}
164676
164736
  </thead>
164677
164737
  <tbody>
164678
164738
  ${viewData.map((itemArg, rowIndex) => {
@@ -164806,6 +164866,13 @@ var _DeesTable = class _DeesTable extends (_a42 = DeesElement, _heading1_dec = [
164806
164866
  </tbody>
164807
164867
  </table>
164808
164868
  </div>
164869
+ <div class="floatingHeader" aria-hidden="true">
164870
+ <table>
164871
+ <thead>
164872
+ ${this.renderHeaderRows(effectiveColumns)}
164873
+ </thead>
164874
+ </table>
164875
+ </div>
164809
164876
  ` : b2` <div class="noDataSet">No data set!</div> `}
164810
164877
  <div slot="footer" class="footer">
164811
164878
  <div class="tableStatistics">
@@ -164840,11 +164907,226 @@ var _DeesTable = class _DeesTable extends (_a42 = DeesElement, _heading1_dec = [
164840
164907
  </dees-tile>
164841
164908
  `;
164842
164909
  }
164910
+ /**
164911
+ * Renders the header rows. Used twice per render: once inside the real
164912
+ * `<thead>` and once inside the floating-header clone, so sort indicators
164913
+ * and filter inputs stay in sync automatically.
164914
+ */
164915
+ renderHeaderRows(effectiveColumns) {
164916
+ return b2`
164917
+ <tr>
164918
+ ${this.selectionMode !== "none" ? b2`
164919
+ <th style="width:42px; text-align:center;">
164920
+ ${this.selectionMode === "multi" ? b2`
164921
+ <dees-input-checkbox
164922
+ .value=${this.areAllVisibleSelected()}
164923
+ .indeterminate=${this.isVisibleSelectionIndeterminate()}
164924
+ @newValue=${(e11) => {
164925
+ e11.stopPropagation();
164926
+ this.setSelectVisible(e11.detail === true);
164927
+ }}
164928
+ ></dees-input-checkbox>
164929
+ ` : b2``}
164930
+ </th>
164931
+ ` : b2``}
164932
+ ${effectiveColumns.filter((c11) => !c11.hidden).map((col) => {
164933
+ const isSortable = !!col.sortable;
164934
+ const ariaSort = this.getAriaSort(col);
164935
+ return b2`
164936
+ <th
164937
+ role="columnheader"
164938
+ aria-sort=${ariaSort}
164939
+ style="${isSortable ? "cursor: pointer;" : ""}"
164940
+ @click=${(eventArg) => isSortable ? this.handleHeaderClick(eventArg, col, effectiveColumns) : null}
164941
+ @contextmenu=${(eventArg) => isSortable ? this.openHeaderContextMenu(eventArg, col, effectiveColumns) : null}
164942
+ >
164943
+ ${col.header ?? col.key}
164944
+ ${this.renderSortIndicator(col)}
164945
+ </th>`;
164946
+ })}
164947
+ ${this.dataActions && this.dataActions.length > 0 ? b2`<th class="actionsCol">Actions</th>` : b2``}
164948
+ </tr>
164949
+ ${this.showColumnFilters ? b2`<tr class="filtersRow">
164950
+ ${this.selectionMode !== "none" ? b2`<th style="width:42px;"></th>` : b2``}
164951
+ ${effectiveColumns.filter((c11) => !c11.hidden).map((col) => {
164952
+ const key2 = String(col.key);
164953
+ if (col.filterable === false) return b2`<th></th>`;
164954
+ return b2`<th>
164955
+ <input type="text" placeholder="Filter..." .value=${this.columnFilters[key2] || ""}
164956
+ @input=${(e11) => this.setColumnFilter(key2, e11.target.value)} />
164957
+ </th>`;
164958
+ })}
164959
+ ${this.dataActions && this.dataActions.length > 0 ? b2`<th></th>` : b2``}
164960
+ </tr>` : b2``}
164961
+ `;
164962
+ }
164963
+ get __floatingHeaderEl() {
164964
+ return this.shadowRoot?.querySelector(".floatingHeader") ?? null;
164965
+ }
164966
+ get __realTableEl() {
164967
+ return this.shadowRoot?.querySelector(".tableScroll > table") ?? null;
164968
+ }
164969
+ get __floatingTableEl() {
164970
+ return this.shadowRoot?.querySelector(".floatingHeader > table") ?? null;
164971
+ }
164972
+ /**
164973
+ * Walks up the DOM (and through shadow roots) collecting every ancestor
164974
+ * element whose computed `overflow-y` makes it a scroll container, plus
164975
+ * `window` at the end. We listen for scroll on all of them so the floating
164976
+ * header reacts whether the user scrolls the page or any nested container.
164977
+ */
164978
+ __collectScrollAncestors() {
164979
+ const result = [];
164980
+ let node2 = this;
164981
+ const scrollish = /* @__PURE__ */ __name((v5) => v5 === "auto" || v5 === "scroll" || v5 === "overlay", "scrollish");
164982
+ while (node2) {
164983
+ if (node2 instanceof Element) {
164984
+ const style2 = getComputedStyle(node2);
164985
+ const sy = scrollish(style2.overflowY);
164986
+ const sx = scrollish(style2.overflowX);
164987
+ if (sy || sx) {
164988
+ result.push({ target: node2, scrollsY: sy, scrollsX: sx });
164989
+ }
164990
+ }
164991
+ const parent = node2.assignedSlot ? node2.assignedSlot : node2.parentNode;
164992
+ if (parent) {
164993
+ node2 = parent;
164994
+ } else if (node2.host) {
164995
+ node2 = node2.host;
164996
+ } else {
164997
+ node2 = null;
164998
+ }
164999
+ }
165000
+ result.push({ target: window, scrollsY: true, scrollsX: true });
165001
+ return result;
165002
+ }
165003
+ /**
165004
+ * Returns the "stick line" — the y-coordinate (in viewport space) at which
165005
+ * the floating header should appear. Defaults to 0 (page top), but if the
165006
+ * table is inside a scroll container we use that container's content-box
165007
+ * top so the header sits inside the container's border/padding instead of
165008
+ * floating over it.
165009
+ */
165010
+ __getStickContext() {
165011
+ let top = 0;
165012
+ let left = 0;
165013
+ let right = window.innerWidth;
165014
+ for (const a5 of this.__scrollAncestors) {
165015
+ if (a5.target === window) continue;
165016
+ const el = a5.target;
165017
+ const r11 = el.getBoundingClientRect();
165018
+ const cs = getComputedStyle(el);
165019
+ if (a5.scrollsY) {
165020
+ const bt = parseFloat(cs.borderTopWidth) || 0;
165021
+ top = Math.max(top, r11.top + bt);
165022
+ }
165023
+ if (a5.scrollsX) {
165024
+ const bl = parseFloat(cs.borderLeftWidth) || 0;
165025
+ const br = parseFloat(cs.borderRightWidth) || 0;
165026
+ left = Math.max(left, r11.left + bl);
165027
+ right = Math.min(right, r11.right - br);
165028
+ }
165029
+ }
165030
+ return { top, left, right };
165031
+ }
165032
+ setupFloatingHeader() {
165033
+ this.teardownFloatingHeader();
165034
+ if (this.fixedHeight) return;
165035
+ const realTable = this.__realTableEl;
165036
+ if (!realTable) return;
165037
+ this.__scrollAncestors = this.__collectScrollAncestors();
165038
+ const tableScrollEl = this.shadowRoot?.querySelector(".tableScroll");
165039
+ if (tableScrollEl) {
165040
+ this.__scrollAncestors.unshift({ target: tableScrollEl, scrollsY: false, scrollsX: true });
165041
+ }
165042
+ this.__floatingResizeObserver = new ResizeObserver(() => {
165043
+ this.__syncFloatingHeader();
165044
+ });
165045
+ this.__floatingResizeObserver.observe(realTable);
165046
+ this.__floatingScrollHandler = () => this.__syncFloatingHeader();
165047
+ for (const a5 of this.__scrollAncestors) {
165048
+ a5.target.addEventListener("scroll", this.__floatingScrollHandler, { passive: true });
165049
+ }
165050
+ window.addEventListener("resize", this.__floatingScrollHandler, { passive: true });
165051
+ this.__syncFloatingHeader();
165052
+ }
165053
+ teardownFloatingHeader() {
165054
+ this.__floatingResizeObserver?.disconnect();
165055
+ this.__floatingResizeObserver = void 0;
165056
+ if (this.__floatingScrollHandler) {
165057
+ for (const a5 of this.__scrollAncestors) {
165058
+ a5.target.removeEventListener("scroll", this.__floatingScrollHandler);
165059
+ }
165060
+ window.removeEventListener("resize", this.__floatingScrollHandler);
165061
+ this.__floatingScrollHandler = void 0;
165062
+ }
165063
+ this.__scrollAncestors = [];
165064
+ this.__floatingActive = false;
165065
+ const fh = this.__floatingHeaderEl;
165066
+ if (fh) fh.classList.remove("active");
165067
+ }
165068
+ /**
165069
+ * Single function that drives both activation and geometry of the floating
165070
+ * header. Called on scroll, resize, table-resize, and after each render.
165071
+ */
165072
+ __syncFloatingHeader() {
165073
+ const fh = this.__floatingHeaderEl;
165074
+ const realTable = this.__realTableEl;
165075
+ const floatTable = this.__floatingTableEl;
165076
+ if (!fh || !realTable || !floatTable) return;
165077
+ const tableRect = realTable.getBoundingClientRect();
165078
+ const stick = this.__getStickContext();
165079
+ floatTable.style.tableLayout = realTable.style.tableLayout || "auto";
165080
+ const realHeadRows = realTable.tHead?.rows;
165081
+ const floatHeadRows = floatTable.tHead?.rows;
165082
+ let headerHeight = 0;
165083
+ if (realHeadRows && floatHeadRows) {
165084
+ for (let r11 = 0; r11 < realHeadRows.length && r11 < floatHeadRows.length; r11++) {
165085
+ headerHeight += realHeadRows[r11].getBoundingClientRect().height;
165086
+ const realCells = realHeadRows[r11].cells;
165087
+ const floatCells = floatHeadRows[r11].cells;
165088
+ for (let c11 = 0; c11 < realCells.length && c11 < floatCells.length; c11++) {
165089
+ const w4 = realCells[c11].getBoundingClientRect().width;
165090
+ floatCells[c11].style.width = `${w4}px`;
165091
+ floatCells[c11].style.minWidth = `${w4}px`;
165092
+ floatCells[c11].style.maxWidth = `${w4}px`;
165093
+ }
165094
+ }
165095
+ }
165096
+ const shouldBeActive = tableRect.top < stick.top && tableRect.bottom > stick.top + Math.min(headerHeight, 1);
165097
+ if (shouldBeActive !== this.__floatingActive) {
165098
+ this.__floatingActive = shouldBeActive;
165099
+ fh.classList.toggle("active", shouldBeActive);
165100
+ }
165101
+ if (!shouldBeActive) return;
165102
+ const clipLeft = Math.max(tableRect.left, stick.left);
165103
+ const clipRight = Math.min(tableRect.right, stick.right);
165104
+ const clipWidth = Math.max(0, clipRight - clipLeft);
165105
+ fh.style.top = `${stick.top}px`;
165106
+ fh.style.left = `${clipLeft}px`;
165107
+ fh.style.width = `${clipWidth}px`;
165108
+ floatTable.style.width = `${tableRect.width}px`;
165109
+ floatTable.style.marginLeft = `${tableRect.left - clipLeft}px`;
165110
+ }
165111
+ async disconnectedCallback() {
165112
+ super.disconnectedCallback();
165113
+ this.teardownFloatingHeader();
165114
+ }
164843
165115
  async firstUpdated() {
164844
165116
  }
164845
165117
  async updated(changedProperties) {
164846
165118
  super.updated(changedProperties);
164847
165119
  this.determineColumnWidths();
165120
+ if (changedProperties.has("fixedHeight") || changedProperties.has("data") || changedProperties.has("columns") || !this.__floatingScrollHandler) {
165121
+ if (!this.fixedHeight && this.data.length > 0) {
165122
+ this.setupFloatingHeader();
165123
+ } else {
165124
+ this.teardownFloatingHeader();
165125
+ }
165126
+ }
165127
+ if (!this.fixedHeight && this.data.length > 0) {
165128
+ this.__syncFloatingHeader();
165129
+ }
164848
165130
  if (this.searchable) {
164849
165131
  const existing = this.dataActions.find((actionArg) => actionArg.type?.includes("header") && actionArg.name === "Search");
164850
165132
  if (!existing) {
@@ -164941,33 +165223,302 @@ var _DeesTable = class _DeesTable extends (_a42 = DeesElement, _heading1_dec = [
164941
165223
  table2.style.tableLayout = "fixed";
164942
165224
  }
164943
165225
  // compute helpers moved to ./data.ts
164944
- toggleSort(col) {
164945
- const key2 = String(col.key);
164946
- if (this.sortKey !== key2) {
164947
- this.sortKey = key2;
164948
- this.sortDir = "asc";
164949
- } else {
164950
- if (this.sortDir === "asc") this.sortDir = "desc";
164951
- else if (this.sortDir === "desc") {
164952
- this.sortDir = null;
164953
- this.sortKey = void 0;
164954
- } else this.sortDir = "asc";
164955
- }
165226
+ // ─── sort: public API ────────────────────────────────────────────────
165227
+ /** Returns the descriptor for `key` if the column is currently in the cascade. */
165228
+ getSortDescriptor(key2) {
165229
+ return this.sortBy.find((d6) => d6.key === key2);
165230
+ }
165231
+ /** Returns the 0-based priority of `key` in the cascade, or -1 if not present. */
165232
+ getSortPriority(key2) {
165233
+ return this.sortBy.findIndex((d6) => d6.key === key2);
165234
+ }
165235
+ /** Replaces the cascade with a single sort entry. */
165236
+ setSort(key2, dir) {
165237
+ this.sortBy = [{ key: key2, dir }];
165238
+ this.emitSortChange();
165239
+ this.requestUpdate();
165240
+ }
165241
+ /**
165242
+ * Inserts (or moves) `key` to a 0-based position in the cascade. If the key is
165243
+ * already present elsewhere, its previous entry is removed before insertion so
165244
+ * a column appears at most once.
165245
+ */
165246
+ addSortAt(key2, position3, dir) {
165247
+ const next2 = this.sortBy.filter((d6) => d6.key !== key2);
165248
+ const clamped = Math.max(0, Math.min(position3, next2.length));
165249
+ next2.splice(clamped, 0, { key: key2, dir });
165250
+ this.sortBy = next2;
165251
+ this.emitSortChange();
165252
+ this.requestUpdate();
165253
+ }
165254
+ /** Appends `key` to the end of the cascade (or moves it there if already present). */
165255
+ appendSort(key2, dir) {
165256
+ const next2 = this.sortBy.filter((d6) => d6.key !== key2);
165257
+ next2.push({ key: key2, dir });
165258
+ this.sortBy = next2;
165259
+ this.emitSortChange();
165260
+ this.requestUpdate();
165261
+ }
165262
+ /** Removes `key` from the cascade. No-op if not present. */
165263
+ removeSort(key2) {
165264
+ if (!this.sortBy.some((d6) => d6.key === key2)) return;
165265
+ this.sortBy = this.sortBy.filter((d6) => d6.key !== key2);
165266
+ this.emitSortChange();
165267
+ this.requestUpdate();
165268
+ }
165269
+ /** Empties the cascade. */
165270
+ clearSorts() {
165271
+ if (this.sortBy.length === 0) return;
165272
+ this.sortBy = [];
165273
+ this.emitSortChange();
165274
+ this.requestUpdate();
165275
+ }
165276
+ emitSortChange() {
164956
165277
  this.dispatchEvent(
164957
165278
  new CustomEvent("sortChange", {
164958
- detail: { key: this.sortKey, dir: this.sortDir },
165279
+ detail: { sortBy: this.sortBy.map((d6) => ({ ...d6 })) },
164959
165280
  bubbles: true
164960
165281
  })
164961
165282
  );
164962
- this.requestUpdate();
164963
165283
  }
165284
+ // ─── sort: header interaction handlers ───────────────────────────────
165285
+ /**
165286
+ * Plain left-click on a sortable header. Cycles `none → asc → desc → none`
165287
+ * collapsing the cascade to a single column. If a multi-column cascade is
165288
+ * active, asks the user to confirm the destructive replacement first. A
165289
+ * Shift+click bypasses the modal and routes through the multi-sort cycle.
165290
+ */
165291
+ async handleHeaderClick(eventArg, col, _effectiveColumns) {
165292
+ if (eventArg.shiftKey) {
165293
+ this.handleHeaderShiftClick(col);
165294
+ return;
165295
+ }
165296
+ const proceed = await this.confirmReplaceCascade(col);
165297
+ if (!proceed) return;
165298
+ this.cycleSingleSort(col);
165299
+ }
165300
+ /**
165301
+ * Cycles a single column through `none → asc → desc → none`, collapsing the
165302
+ * cascade. Used by both plain click and the menu's "Sort Ascending/Descending"
165303
+ * shortcuts (after confirmation).
165304
+ */
165305
+ cycleSingleSort(col) {
165306
+ const key2 = String(col.key);
165307
+ const current = this.sortBy.length === 1 && this.sortBy[0].key === key2 ? this.sortBy[0].dir : null;
165308
+ if (current === "asc") this.setSort(key2, "desc");
165309
+ else if (current === "desc") this.clearSorts();
165310
+ else this.setSort(key2, "asc");
165311
+ }
165312
+ /**
165313
+ * Shift+click cycle on a sortable header. Edits the cascade in place without
165314
+ * destroying other sort keys: append → flip dir → remove.
165315
+ */
165316
+ handleHeaderShiftClick(col) {
165317
+ const key2 = String(col.key);
165318
+ const existing = this.getSortDescriptor(key2);
165319
+ if (!existing) {
165320
+ this.appendSort(key2, "asc");
165321
+ } else if (existing.dir === "asc") {
165322
+ this.sortBy = this.sortBy.map((d6) => d6.key === key2 ? { key: key2, dir: "desc" } : d6);
165323
+ this.emitSortChange();
165324
+ this.requestUpdate();
165325
+ } else {
165326
+ this.removeSort(key2);
165327
+ }
165328
+ }
165329
+ /**
165330
+ * Opens a confirmation modal when the cascade has more than one entry and the
165331
+ * user attempts a destructive single-sort replacement. Resolves to `true` if
165332
+ * the user accepts, `false` if they cancel. If the cascade has 0 or 1 entries
165333
+ * the modal is skipped and we resolve to `true` immediately.
165334
+ */
165335
+ confirmReplaceCascade(targetCol) {
165336
+ if (this.sortBy.length <= 1) return Promise.resolve(true);
165337
+ return new Promise((resolve2) => {
165338
+ let settled = false;
165339
+ const settle = /* @__PURE__ */ __name((result) => {
165340
+ if (settled) return;
165341
+ settled = true;
165342
+ resolve2(result);
165343
+ }, "settle");
165344
+ const summary = this.sortBy.map((d6, i11) => {
165345
+ const c11 = this._lookupColumnByKey?.(d6.key);
165346
+ const label = c11?.header ?? d6.key;
165347
+ return b2`<li>${i11 + 1}. ${label} ${d6.dir === "asc" ? "\u25B2" : "\u25BC"}</li>`;
165348
+ });
165349
+ DeesModal.createAndShow({
165350
+ heading: "Replace multi-column sort?",
165351
+ width: "small",
165352
+ showCloseButton: true,
165353
+ content: b2`
165354
+ <div style="font-size:13px; line-height:1.55;">
165355
+ <p style="margin:0 0 8px;">
165356
+ You currently have a ${this.sortBy.length}-column sort active:
165357
+ </p>
165358
+ <ul style="margin:0 0 12px; padding-left:18px;">${summary}</ul>
165359
+ <p style="margin:0;">
165360
+ Continuing will discard the cascade and replace it with a single sort on
165361
+ <strong>${targetCol.header ?? String(targetCol.key)}</strong>.
165362
+ </p>
165363
+ </div>
165364
+ `,
165365
+ menuOptions: [
165366
+ {
165367
+ name: "Cancel",
165368
+ iconName: "lucide:x",
165369
+ action: /* @__PURE__ */ __name(async (modal) => {
165370
+ settle(false);
165371
+ await modal.destroy();
165372
+ return null;
165373
+ }, "action")
165374
+ },
165375
+ {
165376
+ name: "Replace",
165377
+ iconName: "lucide:check",
165378
+ action: /* @__PURE__ */ __name(async (modal) => {
165379
+ settle(true);
165380
+ await modal.destroy();
165381
+ return null;
165382
+ }, "action")
165383
+ }
165384
+ ]
165385
+ });
165386
+ });
165387
+ }
165388
+ /**
165389
+ * Looks up a column by its string key in the currently effective column set.
165390
+ * Used by the modal helper to render human-friendly labels.
165391
+ */
165392
+ _lookupColumnByKey(key2) {
165393
+ const usingColumns = Array.isArray(this.columns) && this.columns.length > 0;
165394
+ const effective = usingColumns ? computeEffectiveColumns(this.columns, this.augmentFromDisplayFunction, this.displayFunction, this.data) : computeColumnsFromDisplayFunction(this.displayFunction, this.data);
165395
+ return effective.find((c11) => String(c11.key) === key2);
165396
+ }
165397
+ /**
165398
+ * Opens the header context menu for explicit multi-sort priority control.
165399
+ */
165400
+ openHeaderContextMenu(eventArg, col, effectiveColumns) {
165401
+ const items = this.buildHeaderMenuItems(col, effectiveColumns);
165402
+ DeesContextmenu.openContextMenuWithOptions(eventArg, items);
165403
+ }
165404
+ /**
165405
+ * Builds the dynamic context-menu structure for a single column header.
165406
+ */
165407
+ buildHeaderMenuItems(col, effectiveColumns) {
165408
+ const key2 = String(col.key);
165409
+ const existing = this.getSortDescriptor(key2);
165410
+ const cascadeLen = this.sortBy.length;
165411
+ const sortableColumnCount = effectiveColumns.filter((c11) => !!c11.sortable).length;
165412
+ const maxSlot = Math.min(
165413
+ Math.max(cascadeLen + (existing ? 0 : 1), 1),
165414
+ Math.max(sortableColumnCount, 1)
165415
+ );
165416
+ const items = [];
165417
+ items.push({
165418
+ name: "Sort Ascending",
165419
+ iconName: cascadeLen === 1 && existing?.dir === "asc" ? "lucide:check" : "lucide:arrowUp",
165420
+ action: /* @__PURE__ */ __name(async () => {
165421
+ if (await this.confirmReplaceCascade(col)) this.setSort(key2, "asc");
165422
+ return null;
165423
+ }, "action")
165424
+ });
165425
+ items.push({
165426
+ name: "Sort Descending",
165427
+ iconName: cascadeLen === 1 && existing?.dir === "desc" ? "lucide:check" : "lucide:arrowDown",
165428
+ action: /* @__PURE__ */ __name(async () => {
165429
+ if (await this.confirmReplaceCascade(col)) this.setSort(key2, "desc");
165430
+ return null;
165431
+ }, "action")
165432
+ });
165433
+ items.push({ divider: true });
165434
+ for (let slot = 1; slot <= maxSlot; slot++) {
165435
+ const ordinal = ordinalLabel(slot);
165436
+ const isCurrentSlot = existing && this.getSortPriority(key2) === slot - 1;
165437
+ items.push({
165438
+ name: `Set as ${ordinal} sort`,
165439
+ iconName: isCurrentSlot ? "lucide:check" : "lucide:listOrdered",
165440
+ submenu: [
165441
+ {
165442
+ name: "Ascending",
165443
+ iconName: "lucide:arrowUp",
165444
+ action: /* @__PURE__ */ __name(async () => {
165445
+ this.addSortAt(key2, slot - 1, "asc");
165446
+ return null;
165447
+ }, "action")
165448
+ },
165449
+ {
165450
+ name: "Descending",
165451
+ iconName: "lucide:arrowDown",
165452
+ action: /* @__PURE__ */ __name(async () => {
165453
+ this.addSortAt(key2, slot - 1, "desc");
165454
+ return null;
165455
+ }, "action")
165456
+ }
165457
+ ]
165458
+ });
165459
+ }
165460
+ items.push({ divider: true });
165461
+ items.push({
165462
+ name: "Append to sort",
165463
+ iconName: "lucide:plus",
165464
+ submenu: [
165465
+ {
165466
+ name: "Ascending",
165467
+ iconName: "lucide:arrowUp",
165468
+ action: /* @__PURE__ */ __name(async () => {
165469
+ this.appendSort(key2, "asc");
165470
+ return null;
165471
+ }, "action")
165472
+ },
165473
+ {
165474
+ name: "Descending",
165475
+ iconName: "lucide:arrowDown",
165476
+ action: /* @__PURE__ */ __name(async () => {
165477
+ this.appendSort(key2, "desc");
165478
+ return null;
165479
+ }, "action")
165480
+ }
165481
+ ]
165482
+ });
165483
+ if (existing) {
165484
+ items.push({ divider: true });
165485
+ items.push({
165486
+ name: "Remove from sort",
165487
+ iconName: "lucide:minus",
165488
+ action: /* @__PURE__ */ __name(async () => {
165489
+ this.removeSort(key2);
165490
+ return null;
165491
+ }, "action")
165492
+ });
165493
+ }
165494
+ if (cascadeLen > 0) {
165495
+ if (!existing) items.push({ divider: true });
165496
+ items.push({
165497
+ name: "Clear all sorts",
165498
+ iconName: "lucide:trash",
165499
+ action: /* @__PURE__ */ __name(async () => {
165500
+ this.clearSorts();
165501
+ return null;
165502
+ }, "action")
165503
+ });
165504
+ }
165505
+ return items;
165506
+ }
165507
+ // ─── sort: indicator + ARIA ──────────────────────────────────────────
164964
165508
  getAriaSort(col) {
164965
- if (String(col.key) !== this.sortKey || !this.sortDir) return "none";
164966
- return this.sortDir === "asc" ? "ascending" : "descending";
165509
+ const primary = this.sortBy[0];
165510
+ if (!primary || primary.key !== String(col.key)) return "none";
165511
+ return primary.dir === "asc" ? "ascending" : "descending";
164967
165512
  }
164968
165513
  renderSortIndicator(col) {
164969
- if (String(col.key) !== this.sortKey || !this.sortDir) return b2``;
164970
- return b2`<span style="margin-left:6px; opacity:0.7;">${this.sortDir === "asc" ? "\u25B2" : "\u25BC"}</span>`;
165514
+ const idx = this.getSortPriority(String(col.key));
165515
+ if (idx < 0) return b2``;
165516
+ const desc = this.sortBy[idx];
165517
+ const arrow2 = desc.dir === "asc" ? "\u25B2" : "\u25BC";
165518
+ if (this.sortBy.length === 1) {
165519
+ return b2`<span class="sortArrow">${arrow2}</span>`;
165520
+ }
165521
+ return b2`<span class="sortArrow">${arrow2}</span><span class="sortBadge">${idx + 1}</span>`;
164971
165522
  }
164972
165523
  // filtering helpers
164973
165524
  setFilterText(value2) {
@@ -165132,12 +165683,11 @@ _editableFields = new WeakMap();
165132
165683
  _showVerticalLines = new WeakMap();
165133
165684
  _showHorizontalLines = new WeakMap();
165134
165685
  _showGrid = new WeakMap();
165135
- _sortKey = new WeakMap();
165136
- _sortDir = new WeakMap();
165686
+ _sortBy = new WeakMap();
165137
165687
  _filterText = new WeakMap();
165138
165688
  _columnFilters = new WeakMap();
165139
165689
  _showColumnFilters = new WeakMap();
165140
- _stickyHeader = new WeakMap();
165690
+ _fixedHeight = new WeakMap();
165141
165691
  _searchMode = new WeakMap();
165142
165692
  _selectionMode = new WeakMap();
165143
165693
  _selectedIds = new WeakMap();
@@ -165161,12 +165711,11 @@ __decorateElement(_init39, 4, "editableFields", _editableFields_dec, _DeesTable,
165161
165711
  __decorateElement(_init39, 4, "showVerticalLines", _showVerticalLines_dec, _DeesTable, _showVerticalLines);
165162
165712
  __decorateElement(_init39, 4, "showHorizontalLines", _showHorizontalLines_dec, _DeesTable, _showHorizontalLines);
165163
165713
  __decorateElement(_init39, 4, "showGrid", _showGrid_dec, _DeesTable, _showGrid);
165164
- __decorateElement(_init39, 4, "sortKey", _sortKey_dec, _DeesTable, _sortKey);
165165
- __decorateElement(_init39, 4, "sortDir", _sortDir_dec, _DeesTable, _sortDir);
165714
+ __decorateElement(_init39, 4, "sortBy", _sortBy_dec, _DeesTable, _sortBy);
165166
165715
  __decorateElement(_init39, 4, "filterText", _filterText_dec, _DeesTable, _filterText);
165167
165716
  __decorateElement(_init39, 4, "columnFilters", _columnFilters_dec, _DeesTable, _columnFilters);
165168
165717
  __decorateElement(_init39, 4, "showColumnFilters", _showColumnFilters_dec, _DeesTable, _showColumnFilters);
165169
- __decorateElement(_init39, 4, "stickyHeader", _stickyHeader_dec, _DeesTable, _stickyHeader);
165718
+ __decorateElement(_init39, 4, "fixedHeight", _fixedHeight_dec, _DeesTable, _fixedHeight);
165170
165719
  __decorateElement(_init39, 4, "searchMode", _searchMode_dec, _DeesTable, _searchMode);
165171
165720
  __decorateElement(_init39, 4, "selectionMode", _selectionMode_dec, _DeesTable, _selectionMode);
165172
165721
  __decorateElement(_init39, 4, "selectedIds", _selectedIds_dec, _DeesTable, _selectedIds);
@@ -199016,7 +199565,7 @@ init_group_runtime();
199016
199565
  // ts_web/00_commitinfo_data.ts
199017
199566
  var commitinfo = {
199018
199567
  name: "@design.estate/dees-catalog",
199019
- version: "3.61.2",
199568
+ version: "3.63.0",
199020
199569
  description: "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript."
199021
199570
  };
199022
199571
  export {
@@ -200989,4 +201538,4 @@ ibantools/jsnext/ibantools.js:
200989
201538
  * @preferred
200990
201539
  *)
200991
201540
  */
200992
- //# sourceMappingURL=bundle-1775472094927.js.map
201541
+ //# sourceMappingURL=bundle-1775570157836.js.map