@acorex/platform 20.8.17 → 20.8.19

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.
@@ -3948,7 +3948,7 @@ class AXPEntityMasterListViewModel {
3948
3948
  this.events$.next({ action: 'refresh' });
3949
3949
  }
3950
3950
  });
3951
- this.sortedFields.set(this.sortableFields());
3951
+ this.sortedFields.set([]);
3952
3952
  void this.syncShowRowIndexColumnSetting();
3953
3953
  this.settings.onLoaded.pipe(takeUntil(this.destroyed)).subscribe(() => {
3954
3954
  void this.syncShowRowIndexColumnSetting();
@@ -4051,10 +4051,8 @@ class AXPEntityMasterListViewModel {
4051
4051
  .sort((a, b) => columns.findIndex((col) => col.name === (a.column?.options?.dataPath ?? a.name)) -
4052
4052
  columns.findIndex((col) => col.name === (b.column?.options?.dataPath ?? b.name))));
4053
4053
  }
4054
- if (Array.isArray(sorts)) {
4055
- // sorts are AXPSortQuery[]; ensure we map by name
4056
- const sortsMap = new Map(sorts.map((s) => [s.name, s.dir]));
4057
- this.sortedFields.update((prev) => prev.map((sf) => ({ ...sf, dir: sortsMap.get(sf.name) || sf.dir })));
4054
+ if (Array.isArray(sorts) && sorts.length) {
4055
+ this.setActiveSortsFromQueries(sorts);
4058
4056
  }
4059
4057
  // Don't override filters if they came from queryParams
4060
4058
  if (Array.isArray(filters) && !this.hasQueryParamsFilters) {
@@ -4569,18 +4567,80 @@ class AXPEntityMasterListViewModel {
4569
4567
  resetSorts() {
4570
4568
  this.applyViewSorts();
4571
4569
  }
4570
+ /**
4571
+ * Applies active sorts in the given order (click order, saved settings, or view defaults).
4572
+ */
4573
+ setActiveSortsFromQueries(sorts) {
4574
+ const sortableByName = new Map(this.sortableFields().map((f) => [f.name, f]));
4575
+ this.sortedFields.set(sorts
4576
+ .filter((s) => s.dir && sortableByName.has(s.name))
4577
+ .map((s) => ({
4578
+ name: s.name,
4579
+ title: sortableByName.get(s.name).title,
4580
+ dir: s.dir,
4581
+ })));
4582
+ }
4572
4583
  applyViewSorts() {
4573
- const viewSorts = this.view().sorts;
4574
- this.sortedFields.update((prev) => {
4575
- const viewSortsMap = new Map(viewSorts.map((vs) => [vs.name, vs]));
4576
- return prev.map((sf) => {
4577
- const updatedSort = viewSortsMap.get(sf.name);
4578
- if (updatedSort) {
4579
- return { ...updatedSort, title: sf.title };
4580
- }
4581
- return sf;
4582
- });
4583
- });
4584
+ const viewSorts = this.view().sorts ?? [];
4585
+ this.setActiveSortsFromQueries(viewSorts);
4586
+ }
4587
+ /**
4588
+ * Active sort direction for a column (synced with toolbar sort UI and dataSource).
4589
+ */
4590
+ getColumnSortDirection(columnName) {
4591
+ const dir = this.sortedFields().find((sf) => sf.name === columnName)?.dir;
4592
+ return dir === 'asc' || dir === 'desc' ? dir : undefined;
4593
+ }
4594
+ /**
4595
+ * 1-based priority index when multiple columns are sorted (Ctrl+click order).
4596
+ */
4597
+ getColumnSortIndex(columnName) {
4598
+ const index = this.sortedFields().findIndex((sf) => sf.name === columnName);
4599
+ return index >= 0 ? index + 1 : undefined;
4600
+ }
4601
+ /** True when active sorts still match the current view defaults (not yet customized). */
4602
+ isViewDefaultSorts(activeSorts) {
4603
+ const viewSorts = this.view().sorts ?? [];
4604
+ if (activeSorts.length !== viewSorts.length) {
4605
+ return false;
4606
+ }
4607
+ return activeSorts.every((s, i) => s.name === viewSorts[i]?.name && s.dir === viewSorts[i]?.dir);
4608
+ }
4609
+ /**
4610
+ * Toggles column sort (asc → desc → none). Without Ctrl, only one column is active.
4611
+ * With Ctrl/Cmd, multiple columns are sorted in click order (first clicked = primary).
4612
+ */
4613
+ async toggleColumnSort(columnName, multiSort) {
4614
+ const fieldMeta = this.sortableFields().find((f) => f.name === columnName);
4615
+ if (!fieldMeta) {
4616
+ return;
4617
+ }
4618
+ let activeSorts = [...this.sortedFields()];
4619
+ const index = activeSorts.findIndex((sf) => sf.name === columnName);
4620
+ const currentDir = index >= 0 ? activeSorts[index].dir : undefined;
4621
+ const newDir = currentDir === 'asc' ? 'desc' : currentDir === 'desc' ? undefined : 'asc';
4622
+ if (!multiSort) {
4623
+ activeSorts = newDir ? [{ name: columnName, title: fieldMeta.title, dir: newDir }] : [];
4624
+ }
4625
+ else if (index >= 0) {
4626
+ const [item] = activeSorts.splice(index, 1);
4627
+ if (newDir) {
4628
+ // Re-click moves column to end so click order can override view/default order.
4629
+ activeSorts.push({ ...item, title: fieldMeta.title, dir: newDir });
4630
+ }
4631
+ }
4632
+ else if (newDir) {
4633
+ if (this.isViewDefaultSorts(activeSorts)) {
4634
+ activeSorts = [{ name: columnName, title: fieldMeta.title, dir: newDir }];
4635
+ }
4636
+ else {
4637
+ activeSorts.push({ name: columnName, title: fieldMeta.title, dir: newDir });
4638
+ }
4639
+ }
4640
+ this.sortedFields.set(activeSorts);
4641
+ this.lastAppliedSortKey = null;
4642
+ await this.saveSettings('sorts', activeSorts.map((s) => ({ name: s.name, dir: s.dir })));
4643
+ await this.applyFilterAndSort();
4584
4644
  }
4585
4645
  //****************** Commands ******************//
4586
4646
  async executeCommand(commandName, data = null) {
@@ -13954,7 +14014,18 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
13954
14014
  this.disabled = computed(() => this.filterMode() ? false : this.options()['disabled'], ...(ngDevMode ? [{ debugName: "disabled" }] : []));
13955
14015
  this.columns = computed(() => this.options()['columns'] ?? [], ...(ngDevMode ? [{ debugName: "columns" }] : []));
13956
14016
  this.textField = computed(() => this.options()['textField'] ?? '', ...(ngDevMode ? [{ debugName: "textField" }] : []));
13957
- this.textTemplate = computed(() => this.options()['textTemplate'] ?? null, ...(ngDevMode ? [{ debugName: "textTemplate" }] : []));
14017
+ /**
14018
+ * Template for select look (`ax-select-box`). Uses `textTemplate` when set, otherwise
14019
+ * falls back to `displayFormat` (same as tagbox). Single braces are expanded to `{{ }}`
14020
+ * for the select box template engine (see `AXPDataListWidgetComponent.textTemplate`).
14021
+ */
14022
+ this.textTemplate = computed(() => {
14023
+ const explicit = this.options()['textTemplate'];
14024
+ if (explicit) {
14025
+ return explicit.replace(/\{/g, '{{').replace(/\}/g, '}}');
14026
+ }
14027
+ return this.displayFormat() ?? null;
14028
+ }, ...(ngDevMode ? [{ debugName: "textTemplate" }] : []));
13958
14029
  this.hasClearButton = computed(() => (!this.filterMode() && this.options()['hasClearButton']) ?? false, ...(ngDevMode ? [{ debugName: "hasClearButton" }] : []));
13959
14030
  this.showItemTooltip = computed(() => this.options()['showItemTooltip'] ?? false, ...(ngDevMode ? [{ debugName: "showItemTooltip" }] : []));
13960
14031
  this.isItemTruncated = computed(() => this.options()['isItemTruncated'] ?? true, ...(ngDevMode ? [{ debugName: "isItemTruncated" }] : []));