@design.estate/dees-catalog 3.61.1 → 3.62.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.
@@ -108515,6 +108515,7 @@ var init_dees_icon = __esm({
108515
108515
  justify-content: center;
108516
108516
  line-height: 1;
108517
108517
  vertical-align: middle;
108518
+ pointer-events: none;
108518
108519
  }
108519
108520
 
108520
108521
  /* Improve rendering performance */
@@ -151352,12 +151353,14 @@ var _DeesInputList = class _DeesInputList extends (_a36 = DeesInputBase, _value_
151352
151353
  this.currentCandidateIndex = -1;
151353
151354
  this.matchingCandidates = [];
151354
151355
  this.validationText = "";
151355
- const input = this.shadowRoot?.querySelector(".add-input");
151356
- if (input) {
151357
- input.value = "";
151358
- input.focus();
151359
- }
151360
151356
  this.emitChange();
151357
+ this.updateComplete.then(() => {
151358
+ const input = this.shadowRoot?.querySelector(".add-input");
151359
+ if (input) {
151360
+ input.value = "";
151361
+ input.focus();
151362
+ }
151363
+ });
151361
151364
  }
151362
151365
  /**
151363
151366
  * Get the full candidate object for an item by its viewKey.
@@ -151388,12 +151391,14 @@ var _DeesInputList = class _DeesInputList extends (_a36 = DeesInputBase, _value_
151388
151391
  this.value = [...this.value, trimmedValue];
151389
151392
  this.inputValue = "";
151390
151393
  this.validationText = "";
151391
- const input = this.shadowRoot?.querySelector(".add-input");
151392
- if (input) {
151393
- input.value = "";
151394
- input.focus();
151395
- }
151396
151394
  this.emitChange();
151395
+ this.updateComplete.then(() => {
151396
+ const input = this.shadowRoot?.querySelector(".add-input");
151397
+ if (input) {
151398
+ input.value = "";
151399
+ input.focus();
151400
+ }
151401
+ });
151397
151402
  }
151398
151403
  startEdit(index3) {
151399
151404
  this.editingIndex = index3;
@@ -163640,6 +163645,44 @@ var demoFunc29 = /* @__PURE__ */ __name(() => b2`
163640
163645
  ></dees-table>
163641
163646
  </div>
163642
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
+
163643
163686
  <div class="demo-section">
163644
163687
  <h2 class="demo-title">Wide Properties + Many Actions</h2>
163645
163688
  <p class="demo-description">A table with many columns and rich actions to stress test layout and sticky Actions.</p>
@@ -164033,6 +164076,32 @@ var tableStyles = [
164033
164076
  color: ${cssManager.bdTheme("hsl(215.4 16.3% 46.9%)", "hsl(215 20.2% 65.1%)")};
164034
164077
  letter-spacing: -0.01em;
164035
164078
  }
164079
+
164080
+ th[role='columnheader']:hover {
164081
+ color: var(--dees-color-text-primary);
164082
+ }
164083
+
164084
+ th .sortArrow {
164085
+ display: inline-block;
164086
+ margin-left: 6px;
164087
+ font-size: 10px;
164088
+ line-height: 1;
164089
+ opacity: 0.7;
164090
+ vertical-align: middle;
164091
+ }
164092
+
164093
+ th .sortBadge {
164094
+ display: inline-block;
164095
+ margin-left: 3px;
164096
+ padding: 1px 5px;
164097
+ font-size: 10px;
164098
+ font-weight: 600;
164099
+ line-height: 1;
164100
+ color: ${cssManager.bdTheme("hsl(222.2 47.4% 30%)", "hsl(217.2 91.2% 75%)")};
164101
+ background: ${cssManager.bdTheme("hsl(222.2 47.4% 51.2% / 0.12)", "hsl(217.2 91.2% 59.8% / 0.18)")};
164102
+ border-radius: 999px;
164103
+ vertical-align: middle;
164104
+ }
164036
164105
 
164037
164106
  :host([show-vertical-lines]) th {
164038
164107
  border-right: 1px solid var(--dees-color-border-default);
@@ -164224,7 +164293,23 @@ function getCellValue(row, col, displayFunction) {
164224
164293
  return col.value ? col.value(row) : row[col.key];
164225
164294
  }
164226
164295
  __name(getCellValue, "getCellValue");
164227
- function getViewData(data, effectiveColumns, sortKey, sortDir, filterText, columnFilters, filterMode = "table", lucenePredicate) {
164296
+ function compareCellValues(va, vb) {
164297
+ if (va == null && vb == null) return 0;
164298
+ if (va == null) return -1;
164299
+ if (vb == null) return 1;
164300
+ if (typeof va === "number" && typeof vb === "number") {
164301
+ if (va < vb) return -1;
164302
+ if (va > vb) return 1;
164303
+ return 0;
164304
+ }
164305
+ const sa = String(va).toLowerCase();
164306
+ const sb = String(vb).toLowerCase();
164307
+ if (sa < sb) return -1;
164308
+ if (sa > sb) return 1;
164309
+ return 0;
164310
+ }
164311
+ __name(compareCellValues, "compareCellValues");
164312
+ function getViewData(data, effectiveColumns, sortBy, filterText, columnFilters, filterMode = "table", lucenePredicate) {
164228
164313
  let arr = data.slice();
164229
164314
  const ft = (filterText || "").trim().toLowerCase();
164230
164315
  const cf = columnFilters || {};
@@ -164238,9 +164323,9 @@ function getViewData(data, effectiveColumns, sortKey, sortDir, filterText, colum
164238
164323
  const needle = String(cf[k4]).toLowerCase();
164239
164324
  if (!s10.includes(needle)) return false;
164240
164325
  } else {
164241
- const col2 = effectiveColumns.find((c11) => String(c11.key) === k4);
164242
- if (!col2 || col2.hidden || col2.filterable === false) continue;
164243
- const val = getCellValue(row, col2);
164326
+ const col = effectiveColumns.find((c11) => String(c11.key) === k4);
164327
+ if (!col || col.hidden || col.filterable === false) continue;
164328
+ const val = getCellValue(row, col);
164244
164329
  const s10 = String(val ?? "").toLowerCase();
164245
164330
  const needle = String(cf[k4]).toLowerCase();
164246
164331
  if (!s10.includes(needle)) return false;
@@ -164261,9 +164346,9 @@ function getViewData(data, effectiveColumns, sortKey, sortDir, filterText, colum
164261
164346
  }
164262
164347
  }
164263
164348
  } else {
164264
- for (const col2 of effectiveColumns) {
164265
- if (col2.hidden) continue;
164266
- const val = getCellValue(row, col2);
164349
+ for (const col of effectiveColumns) {
164350
+ if (col.hidden) continue;
164351
+ const val = getCellValue(row, col);
164267
164352
  const s10 = String(val ?? "").toLowerCase();
164268
164353
  if (s10.includes(ft)) {
164269
164354
  any = true;
@@ -164276,21 +164361,14 @@ function getViewData(data, effectiveColumns, sortKey, sortDir, filterText, colum
164276
164361
  return true;
164277
164362
  });
164278
164363
  }
164279
- if (!sortKey || !sortDir) return arr;
164280
- const col = effectiveColumns.find((c11) => String(c11.key) === sortKey);
164281
- if (!col) return arr;
164282
- const dir = sortDir === "asc" ? 1 : -1;
164364
+ if (!sortBy || sortBy.length === 0) return arr;
164365
+ const resolved2 = sortBy.map((desc) => ({ desc, col: effectiveColumns.find((c11) => String(c11.key) === desc.key) })).filter((entry) => !!entry.col);
164366
+ if (resolved2.length === 0) return arr;
164283
164367
  arr.sort((a5, b5) => {
164284
- const va = getCellValue(a5, col);
164285
- const vb = getCellValue(b5, col);
164286
- if (va == null && vb == null) return 0;
164287
- if (va == null) return -1 * dir;
164288
- if (vb == null) return 1 * dir;
164289
- if (typeof va === "number" && typeof vb === "number") return (va - vb) * dir;
164290
- const sa = String(va).toLowerCase();
164291
- const sb = String(vb).toLowerCase();
164292
- if (sa < sb) return -1 * dir;
164293
- if (sa > sb) return 1 * dir;
164368
+ for (const { desc, col } of resolved2) {
164369
+ const cmp2 = compareCellValues(getCellValue(a5, col), getCellValue(b5, col));
164370
+ if (cmp2 !== 0) return desc.dir === "asc" ? cmp2 : -cmp2;
164371
+ }
164294
164372
  return 0;
164295
164373
  });
164296
164374
  return arr;
@@ -164443,7 +164521,13 @@ __name(compileLucenePredicate, "compileLucenePredicate");
164443
164521
  init_dist_ts30();
164444
164522
  init_dist_ts29();
164445
164523
  init_theme();
164446
- 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;
164524
+ var _selectedIds_dec, _selectionMode_dec, _searchMode_dec, _stickyHeader_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, _stickyHeader, _searchMode, _selectionMode, _selectedIds;
164525
+ function ordinalLabel(n12) {
164526
+ const s10 = ["th", "st", "nd", "rd"];
164527
+ const v5 = n12 % 100;
164528
+ return n12 + (s10[(v5 - 20) % 10] || s10[v5] || s10[0]);
164529
+ }
164530
+ __name(ordinalLabel, "ordinalLabel");
164447
164531
  _DeesTable_decorators = [customElement("dees-table")];
164448
164532
  var _DeesTable = class _DeesTable extends (_a42 = DeesElement, _heading1_dec = [n5({
164449
164533
  type: String
@@ -164486,7 +164570,7 @@ var _DeesTable = class _DeesTable extends (_a42 = DeesElement, _heading1_dec = [
164486
164570
  type: Boolean,
164487
164571
  reflect: true,
164488
164572
  attribute: "show-grid"
164489
- })], _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) {
164573
+ })], _sortBy_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) {
164490
164574
  constructor() {
164491
164575
  super();
164492
164576
  __privateAdd(this, _heading1, __runInitializers(_init39, 8, this, "heading 1")), __runInitializers(_init39, 11, this);
@@ -164513,17 +164597,16 @@ var _DeesTable = class _DeesTable extends (_a42 = DeesElement, _heading1_dec = [
164513
164597
  __publicField(this, "files", []);
164514
164598
  __publicField(this, "fileWeakMap", /* @__PURE__ */ new WeakMap());
164515
164599
  __publicField(this, "dataChangeSubject", new domtools_pluginexports_exports.smartrx.rxjs.Subject());
164516
- __privateAdd(this, _sortKey, __runInitializers(_init39, 88, this)), __runInitializers(_init39, 91, this);
164517
- __privateAdd(this, _sortDir, __runInitializers(_init39, 92, this, null)), __runInitializers(_init39, 95, this);
164518
- __privateAdd(this, _filterText, __runInitializers(_init39, 96, this, "")), __runInitializers(_init39, 99, this);
164519
- __privateAdd(this, _columnFilters, __runInitializers(_init39, 100, this, {})), __runInitializers(_init39, 103, this);
164520
- __privateAdd(this, _showColumnFilters, __runInitializers(_init39, 104, this, false)), __runInitializers(_init39, 107, this);
164521
- __privateAdd(this, _stickyHeader, __runInitializers(_init39, 108, this, false)), __runInitializers(_init39, 111, this);
164522
- __privateAdd(this, _searchMode, __runInitializers(_init39, 112, this, "table")), __runInitializers(_init39, 115, this);
164600
+ __privateAdd(this, _sortBy, __runInitializers(_init39, 88, this, [])), __runInitializers(_init39, 91, this);
164601
+ __privateAdd(this, _filterText, __runInitializers(_init39, 92, this, "")), __runInitializers(_init39, 95, this);
164602
+ __privateAdd(this, _columnFilters, __runInitializers(_init39, 96, this, {})), __runInitializers(_init39, 99, this);
164603
+ __privateAdd(this, _showColumnFilters, __runInitializers(_init39, 100, this, false)), __runInitializers(_init39, 103, this);
164604
+ __privateAdd(this, _stickyHeader, __runInitializers(_init39, 104, this, false)), __runInitializers(_init39, 107, this);
164605
+ __privateAdd(this, _searchMode, __runInitializers(_init39, 108, this, "table")), __runInitializers(_init39, 111, this);
164523
164606
  __publicField(this, "__searchTextSub");
164524
164607
  __publicField(this, "__searchModeSub");
164525
- __privateAdd(this, _selectionMode, __runInitializers(_init39, 116, this, "none")), __runInitializers(_init39, 119, this);
164526
- __privateAdd(this, _selectedIds, __runInitializers(_init39, 120, this, /* @__PURE__ */ new Set())), __runInitializers(_init39, 123, this);
164608
+ __privateAdd(this, _selectionMode, __runInitializers(_init39, 112, this, "none")), __runInitializers(_init39, 115, this);
164609
+ __privateAdd(this, _selectedIds, __runInitializers(_init39, 116, this, /* @__PURE__ */ new Set())), __runInitializers(_init39, 119, this);
164527
164610
  __publicField(this, "_rowIdMap", /* @__PURE__ */ new WeakMap());
164528
164611
  __publicField(this, "_rowIdCounter", 0);
164529
164612
  __publicField(this, "__debounceTimer");
@@ -164544,8 +164627,7 @@ var _DeesTable = class _DeesTable extends (_a42 = DeesElement, _heading1_dec = [
164544
164627
  const viewData = getViewData(
164545
164628
  this.data,
164546
164629
  effectiveColumns,
164547
- this.sortKey,
164548
- this.sortDir,
164630
+ this.sortBy,
164549
164631
  this.filterText,
164550
164632
  this.columnFilters,
164551
164633
  this.searchMode === "data" ? "data" : "table",
@@ -164640,7 +164722,8 @@ var _DeesTable = class _DeesTable extends (_a42 = DeesElement, _heading1_dec = [
164640
164722
  role="columnheader"
164641
164723
  aria-sort=${ariaSort}
164642
164724
  style="${isSortable ? "cursor: pointer;" : ""}"
164643
- @click=${() => isSortable ? this.toggleSort(col) : null}
164725
+ @click=${(eventArg) => isSortable ? this.handleHeaderClick(eventArg, col, effectiveColumns) : null}
164726
+ @contextmenu=${(eventArg) => isSortable ? this.openHeaderContextMenu(eventArg, col, effectiveColumns) : null}
164644
164727
  >
164645
164728
  ${col.header ?? col.key}
164646
164729
  ${this.renderSortIndicator(col)}
@@ -164936,33 +165019,302 @@ var _DeesTable = class _DeesTable extends (_a42 = DeesElement, _heading1_dec = [
164936
165019
  table2.style.tableLayout = "fixed";
164937
165020
  }
164938
165021
  // compute helpers moved to ./data.ts
164939
- toggleSort(col) {
164940
- const key2 = String(col.key);
164941
- if (this.sortKey !== key2) {
164942
- this.sortKey = key2;
164943
- this.sortDir = "asc";
164944
- } else {
164945
- if (this.sortDir === "asc") this.sortDir = "desc";
164946
- else if (this.sortDir === "desc") {
164947
- this.sortDir = null;
164948
- this.sortKey = void 0;
164949
- } else this.sortDir = "asc";
164950
- }
165022
+ // ─── sort: public API ────────────────────────────────────────────────
165023
+ /** Returns the descriptor for `key` if the column is currently in the cascade. */
165024
+ getSortDescriptor(key2) {
165025
+ return this.sortBy.find((d6) => d6.key === key2);
165026
+ }
165027
+ /** Returns the 0-based priority of `key` in the cascade, or -1 if not present. */
165028
+ getSortPriority(key2) {
165029
+ return this.sortBy.findIndex((d6) => d6.key === key2);
165030
+ }
165031
+ /** Replaces the cascade with a single sort entry. */
165032
+ setSort(key2, dir) {
165033
+ this.sortBy = [{ key: key2, dir }];
165034
+ this.emitSortChange();
165035
+ this.requestUpdate();
165036
+ }
165037
+ /**
165038
+ * Inserts (or moves) `key` to a 0-based position in the cascade. If the key is
165039
+ * already present elsewhere, its previous entry is removed before insertion so
165040
+ * a column appears at most once.
165041
+ */
165042
+ addSortAt(key2, position3, dir) {
165043
+ const next2 = this.sortBy.filter((d6) => d6.key !== key2);
165044
+ const clamped = Math.max(0, Math.min(position3, next2.length));
165045
+ next2.splice(clamped, 0, { key: key2, dir });
165046
+ this.sortBy = next2;
165047
+ this.emitSortChange();
165048
+ this.requestUpdate();
165049
+ }
165050
+ /** Appends `key` to the end of the cascade (or moves it there if already present). */
165051
+ appendSort(key2, dir) {
165052
+ const next2 = this.sortBy.filter((d6) => d6.key !== key2);
165053
+ next2.push({ key: key2, dir });
165054
+ this.sortBy = next2;
165055
+ this.emitSortChange();
165056
+ this.requestUpdate();
165057
+ }
165058
+ /** Removes `key` from the cascade. No-op if not present. */
165059
+ removeSort(key2) {
165060
+ if (!this.sortBy.some((d6) => d6.key === key2)) return;
165061
+ this.sortBy = this.sortBy.filter((d6) => d6.key !== key2);
165062
+ this.emitSortChange();
165063
+ this.requestUpdate();
165064
+ }
165065
+ /** Empties the cascade. */
165066
+ clearSorts() {
165067
+ if (this.sortBy.length === 0) return;
165068
+ this.sortBy = [];
165069
+ this.emitSortChange();
165070
+ this.requestUpdate();
165071
+ }
165072
+ emitSortChange() {
164951
165073
  this.dispatchEvent(
164952
165074
  new CustomEvent("sortChange", {
164953
- detail: { key: this.sortKey, dir: this.sortDir },
165075
+ detail: { sortBy: this.sortBy.map((d6) => ({ ...d6 })) },
164954
165076
  bubbles: true
164955
165077
  })
164956
165078
  );
164957
- this.requestUpdate();
164958
165079
  }
165080
+ // ─── sort: header interaction handlers ───────────────────────────────
165081
+ /**
165082
+ * Plain left-click on a sortable header. Cycles `none → asc → desc → none`
165083
+ * collapsing the cascade to a single column. If a multi-column cascade is
165084
+ * active, asks the user to confirm the destructive replacement first. A
165085
+ * Shift+click bypasses the modal and routes through the multi-sort cycle.
165086
+ */
165087
+ async handleHeaderClick(eventArg, col, _effectiveColumns) {
165088
+ if (eventArg.shiftKey) {
165089
+ this.handleHeaderShiftClick(col);
165090
+ return;
165091
+ }
165092
+ const proceed = await this.confirmReplaceCascade(col);
165093
+ if (!proceed) return;
165094
+ this.cycleSingleSort(col);
165095
+ }
165096
+ /**
165097
+ * Cycles a single column through `none → asc → desc → none`, collapsing the
165098
+ * cascade. Used by both plain click and the menu's "Sort Ascending/Descending"
165099
+ * shortcuts (after confirmation).
165100
+ */
165101
+ cycleSingleSort(col) {
165102
+ const key2 = String(col.key);
165103
+ const current = this.sortBy.length === 1 && this.sortBy[0].key === key2 ? this.sortBy[0].dir : null;
165104
+ if (current === "asc") this.setSort(key2, "desc");
165105
+ else if (current === "desc") this.clearSorts();
165106
+ else this.setSort(key2, "asc");
165107
+ }
165108
+ /**
165109
+ * Shift+click cycle on a sortable header. Edits the cascade in place without
165110
+ * destroying other sort keys: append → flip dir → remove.
165111
+ */
165112
+ handleHeaderShiftClick(col) {
165113
+ const key2 = String(col.key);
165114
+ const existing = this.getSortDescriptor(key2);
165115
+ if (!existing) {
165116
+ this.appendSort(key2, "asc");
165117
+ } else if (existing.dir === "asc") {
165118
+ this.sortBy = this.sortBy.map((d6) => d6.key === key2 ? { key: key2, dir: "desc" } : d6);
165119
+ this.emitSortChange();
165120
+ this.requestUpdate();
165121
+ } else {
165122
+ this.removeSort(key2);
165123
+ }
165124
+ }
165125
+ /**
165126
+ * Opens a confirmation modal when the cascade has more than one entry and the
165127
+ * user attempts a destructive single-sort replacement. Resolves to `true` if
165128
+ * the user accepts, `false` if they cancel. If the cascade has 0 or 1 entries
165129
+ * the modal is skipped and we resolve to `true` immediately.
165130
+ */
165131
+ confirmReplaceCascade(targetCol) {
165132
+ if (this.sortBy.length <= 1) return Promise.resolve(true);
165133
+ return new Promise((resolve2) => {
165134
+ let settled = false;
165135
+ const settle = /* @__PURE__ */ __name((result) => {
165136
+ if (settled) return;
165137
+ settled = true;
165138
+ resolve2(result);
165139
+ }, "settle");
165140
+ const summary = this.sortBy.map((d6, i11) => {
165141
+ const c11 = this._lookupColumnByKey?.(d6.key);
165142
+ const label = c11?.header ?? d6.key;
165143
+ return b2`<li>${i11 + 1}. ${label} ${d6.dir === "asc" ? "\u25B2" : "\u25BC"}</li>`;
165144
+ });
165145
+ DeesModal.createAndShow({
165146
+ heading: "Replace multi-column sort?",
165147
+ width: "small",
165148
+ showCloseButton: true,
165149
+ content: b2`
165150
+ <div style="font-size:13px; line-height:1.55;">
165151
+ <p style="margin:0 0 8px;">
165152
+ You currently have a ${this.sortBy.length}-column sort active:
165153
+ </p>
165154
+ <ul style="margin:0 0 12px; padding-left:18px;">${summary}</ul>
165155
+ <p style="margin:0;">
165156
+ Continuing will discard the cascade and replace it with a single sort on
165157
+ <strong>${targetCol.header ?? String(targetCol.key)}</strong>.
165158
+ </p>
165159
+ </div>
165160
+ `,
165161
+ menuOptions: [
165162
+ {
165163
+ name: "Cancel",
165164
+ iconName: "lucide:x",
165165
+ action: /* @__PURE__ */ __name(async (modal) => {
165166
+ settle(false);
165167
+ await modal.destroy();
165168
+ return null;
165169
+ }, "action")
165170
+ },
165171
+ {
165172
+ name: "Replace",
165173
+ iconName: "lucide:check",
165174
+ action: /* @__PURE__ */ __name(async (modal) => {
165175
+ settle(true);
165176
+ await modal.destroy();
165177
+ return null;
165178
+ }, "action")
165179
+ }
165180
+ ]
165181
+ });
165182
+ });
165183
+ }
165184
+ /**
165185
+ * Looks up a column by its string key in the currently effective column set.
165186
+ * Used by the modal helper to render human-friendly labels.
165187
+ */
165188
+ _lookupColumnByKey(key2) {
165189
+ const usingColumns = Array.isArray(this.columns) && this.columns.length > 0;
165190
+ const effective = usingColumns ? computeEffectiveColumns(this.columns, this.augmentFromDisplayFunction, this.displayFunction, this.data) : computeColumnsFromDisplayFunction(this.displayFunction, this.data);
165191
+ return effective.find((c11) => String(c11.key) === key2);
165192
+ }
165193
+ /**
165194
+ * Opens the header context menu for explicit multi-sort priority control.
165195
+ */
165196
+ openHeaderContextMenu(eventArg, col, effectiveColumns) {
165197
+ const items = this.buildHeaderMenuItems(col, effectiveColumns);
165198
+ DeesContextmenu.openContextMenuWithOptions(eventArg, items);
165199
+ }
165200
+ /**
165201
+ * Builds the dynamic context-menu structure for a single column header.
165202
+ */
165203
+ buildHeaderMenuItems(col, effectiveColumns) {
165204
+ const key2 = String(col.key);
165205
+ const existing = this.getSortDescriptor(key2);
165206
+ const cascadeLen = this.sortBy.length;
165207
+ const sortableColumnCount = effectiveColumns.filter((c11) => !!c11.sortable).length;
165208
+ const maxSlot = Math.min(
165209
+ Math.max(cascadeLen + (existing ? 0 : 1), 1),
165210
+ Math.max(sortableColumnCount, 1)
165211
+ );
165212
+ const items = [];
165213
+ items.push({
165214
+ name: "Sort Ascending",
165215
+ iconName: cascadeLen === 1 && existing?.dir === "asc" ? "lucide:check" : "lucide:arrowUp",
165216
+ action: /* @__PURE__ */ __name(async () => {
165217
+ if (await this.confirmReplaceCascade(col)) this.setSort(key2, "asc");
165218
+ return null;
165219
+ }, "action")
165220
+ });
165221
+ items.push({
165222
+ name: "Sort Descending",
165223
+ iconName: cascadeLen === 1 && existing?.dir === "desc" ? "lucide:check" : "lucide:arrowDown",
165224
+ action: /* @__PURE__ */ __name(async () => {
165225
+ if (await this.confirmReplaceCascade(col)) this.setSort(key2, "desc");
165226
+ return null;
165227
+ }, "action")
165228
+ });
165229
+ items.push({ divider: true });
165230
+ for (let slot = 1; slot <= maxSlot; slot++) {
165231
+ const ordinal = ordinalLabel(slot);
165232
+ const isCurrentSlot = existing && this.getSortPriority(key2) === slot - 1;
165233
+ items.push({
165234
+ name: `Set as ${ordinal} sort`,
165235
+ iconName: isCurrentSlot ? "lucide:check" : "lucide:listOrdered",
165236
+ submenu: [
165237
+ {
165238
+ name: "Ascending",
165239
+ iconName: "lucide:arrowUp",
165240
+ action: /* @__PURE__ */ __name(async () => {
165241
+ this.addSortAt(key2, slot - 1, "asc");
165242
+ return null;
165243
+ }, "action")
165244
+ },
165245
+ {
165246
+ name: "Descending",
165247
+ iconName: "lucide:arrowDown",
165248
+ action: /* @__PURE__ */ __name(async () => {
165249
+ this.addSortAt(key2, slot - 1, "desc");
165250
+ return null;
165251
+ }, "action")
165252
+ }
165253
+ ]
165254
+ });
165255
+ }
165256
+ items.push({ divider: true });
165257
+ items.push({
165258
+ name: "Append to sort",
165259
+ iconName: "lucide:plus",
165260
+ submenu: [
165261
+ {
165262
+ name: "Ascending",
165263
+ iconName: "lucide:arrowUp",
165264
+ action: /* @__PURE__ */ __name(async () => {
165265
+ this.appendSort(key2, "asc");
165266
+ return null;
165267
+ }, "action")
165268
+ },
165269
+ {
165270
+ name: "Descending",
165271
+ iconName: "lucide:arrowDown",
165272
+ action: /* @__PURE__ */ __name(async () => {
165273
+ this.appendSort(key2, "desc");
165274
+ return null;
165275
+ }, "action")
165276
+ }
165277
+ ]
165278
+ });
165279
+ if (existing) {
165280
+ items.push({ divider: true });
165281
+ items.push({
165282
+ name: "Remove from sort",
165283
+ iconName: "lucide:minus",
165284
+ action: /* @__PURE__ */ __name(async () => {
165285
+ this.removeSort(key2);
165286
+ return null;
165287
+ }, "action")
165288
+ });
165289
+ }
165290
+ if (cascadeLen > 0) {
165291
+ if (!existing) items.push({ divider: true });
165292
+ items.push({
165293
+ name: "Clear all sorts",
165294
+ iconName: "lucide:trash",
165295
+ action: /* @__PURE__ */ __name(async () => {
165296
+ this.clearSorts();
165297
+ return null;
165298
+ }, "action")
165299
+ });
165300
+ }
165301
+ return items;
165302
+ }
165303
+ // ─── sort: indicator + ARIA ──────────────────────────────────────────
164959
165304
  getAriaSort(col) {
164960
- if (String(col.key) !== this.sortKey || !this.sortDir) return "none";
164961
- return this.sortDir === "asc" ? "ascending" : "descending";
165305
+ const primary = this.sortBy[0];
165306
+ if (!primary || primary.key !== String(col.key)) return "none";
165307
+ return primary.dir === "asc" ? "ascending" : "descending";
164962
165308
  }
164963
165309
  renderSortIndicator(col) {
164964
- if (String(col.key) !== this.sortKey || !this.sortDir) return b2``;
164965
- return b2`<span style="margin-left:6px; opacity:0.7;">${this.sortDir === "asc" ? "\u25B2" : "\u25BC"}</span>`;
165310
+ const idx = this.getSortPriority(String(col.key));
165311
+ if (idx < 0) return b2``;
165312
+ const desc = this.sortBy[idx];
165313
+ const arrow2 = desc.dir === "asc" ? "\u25B2" : "\u25BC";
165314
+ if (this.sortBy.length === 1) {
165315
+ return b2`<span class="sortArrow">${arrow2}</span>`;
165316
+ }
165317
+ return b2`<span class="sortArrow">${arrow2}</span><span class="sortBadge">${idx + 1}</span>`;
164966
165318
  }
164967
165319
  // filtering helpers
164968
165320
  setFilterText(value2) {
@@ -165127,8 +165479,7 @@ _editableFields = new WeakMap();
165127
165479
  _showVerticalLines = new WeakMap();
165128
165480
  _showHorizontalLines = new WeakMap();
165129
165481
  _showGrid = new WeakMap();
165130
- _sortKey = new WeakMap();
165131
- _sortDir = new WeakMap();
165482
+ _sortBy = new WeakMap();
165132
165483
  _filterText = new WeakMap();
165133
165484
  _columnFilters = new WeakMap();
165134
165485
  _showColumnFilters = new WeakMap();
@@ -165156,8 +165507,7 @@ __decorateElement(_init39, 4, "editableFields", _editableFields_dec, _DeesTable,
165156
165507
  __decorateElement(_init39, 4, "showVerticalLines", _showVerticalLines_dec, _DeesTable, _showVerticalLines);
165157
165508
  __decorateElement(_init39, 4, "showHorizontalLines", _showHorizontalLines_dec, _DeesTable, _showHorizontalLines);
165158
165509
  __decorateElement(_init39, 4, "showGrid", _showGrid_dec, _DeesTable, _showGrid);
165159
- __decorateElement(_init39, 4, "sortKey", _sortKey_dec, _DeesTable, _sortKey);
165160
- __decorateElement(_init39, 4, "sortDir", _sortDir_dec, _DeesTable, _sortDir);
165510
+ __decorateElement(_init39, 4, "sortBy", _sortBy_dec, _DeesTable, _sortBy);
165161
165511
  __decorateElement(_init39, 4, "filterText", _filterText_dec, _DeesTable, _filterText);
165162
165512
  __decorateElement(_init39, 4, "columnFilters", _columnFilters_dec, _DeesTable, _columnFilters);
165163
165513
  __decorateElement(_init39, 4, "showColumnFilters", _showColumnFilters_dec, _DeesTable, _showColumnFilters);
@@ -199011,7 +199361,7 @@ init_group_runtime();
199011
199361
  // ts_web/00_commitinfo_data.ts
199012
199362
  var commitinfo = {
199013
199363
  name: "@design.estate/dees-catalog",
199014
- version: "3.61.1",
199364
+ version: "3.62.0",
199015
199365
  description: "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript."
199016
199366
  };
199017
199367
  export {
@@ -200984,4 +201334,4 @@ ibantools/jsnext/ibantools.js:
200984
201334
  * @preferred
200985
201335
  *)
200986
201336
  */
200987
- //# sourceMappingURL=bundle-1775387335721.js.map
201337
+ //# sourceMappingURL=bundle-1775564414196.js.map
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@design.estate/dees-catalog',
6
- version: '3.61.1',
6
+ version: '3.62.0',
7
7
  description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfd2ViLzAwX2NvbW1pdGluZm9fZGF0YS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRztJQUN4QixJQUFJLEVBQUUsNkJBQTZCO0lBQ25DLE9BQU8sRUFBRSxRQUFRO0lBQ2pCLFdBQVcsRUFBRSxzSkFBc0o7Q0FDcEssQ0FBQSJ9
@@ -1,5 +1,11 @@
1
- import type { Column, TDisplayFunction } from './types.js';
1
+ import type { Column, ISortDescriptor, TDisplayFunction } from './types.js';
2
2
  export declare function computeColumnsFromDisplayFunction<T>(displayFunction: TDisplayFunction<T>, data: T[]): Column<T>[];
3
3
  export declare function computeEffectiveColumns<T>(columns: Column<T>[] | undefined, augmentFromDisplayFunction: boolean, displayFunction: TDisplayFunction<T>, data: T[]): Column<T>[];
4
4
  export declare function getCellValue<T>(row: T, col: Column<T>, displayFunction?: TDisplayFunction<T>): any;
5
- export declare function getViewData<T>(data: T[], effectiveColumns: Column<T>[], sortKey?: string, sortDir?: 'asc' | 'desc' | null, filterText?: string, columnFilters?: Record<string, string>, filterMode?: 'table' | 'data', lucenePredicate?: (row: T) => boolean): T[];
5
+ /**
6
+ * Compares two cell values in ascending order. Returns -1, 0, or 1.
7
+ * Null/undefined values sort before defined values. Numbers compare numerically;
8
+ * everything else compares as case-insensitive strings.
9
+ */
10
+ export declare function compareCellValues(va: any, vb: any): number;
11
+ export declare function getViewData<T>(data: T[], effectiveColumns: Column<T>[], sortBy: ISortDescriptor[], filterText?: string, columnFilters?: Record<string, string>, filterMode?: 'table' | 'data', lucenePredicate?: (row: T) => boolean): T[];