@fuentis/phoenix-ui 0.0.9-alpha.637 → 0.0.9-alpha.639

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.
@@ -2009,6 +2009,8 @@ class TableCaptionComponent {
2009
2009
  /** Local storage keys */
2010
2010
  FILTER_KEY = '';
2011
2011
  COLUMN_KEY = '';
2012
+ /** Live subscription to the current filter form; torn down on each rebuild. */
2013
+ filtersValueSub;
2012
2014
  /**
2013
2015
  * When table detects that selected filter values no longer exist in data,
2014
2016
  * it passes the remaining valid values here so table-caption can prune its form.
@@ -2032,8 +2034,17 @@ class TableCaptionComponent {
2032
2034
  }
2033
2035
  if (!this.tableConfiguration)
2034
2036
  return;
2035
- this.FILTER_KEY = `filters_${this.tableConfiguration.key ?? 'table'}`;
2036
- this.COLUMN_KEY = `columns_${this.tableConfiguration.key ?? 'table'}`;
2037
+ // Storage key: prefer an explicit `stateKey` (unique per table view; avoids
2038
+ // the collisions where `key` doubles as the row-id field, e.g. two tables
2039
+ // sharing `filters_controls`), fall back to `key`. `stateKeyPrefix` (e.g. the
2040
+ // current user id) scopes state per user so a shared machine does not leak
2041
+ // one user's filters/columns to another.
2042
+ const stateBase = this.tableConfiguration.stateKey ?? this.tableConfiguration.key ?? 'table';
2043
+ const statePrefix = this.tableConfiguration.stateKeyPrefix
2044
+ ? `${this.tableConfiguration.stateKeyPrefix}.`
2045
+ : '';
2046
+ this.FILTER_KEY = `filters_${statePrefix}${stateBase}`;
2047
+ this.COLUMN_KEY = `columns_${statePrefix}${stateBase}`;
2037
2048
  // Default visible columns: all non-hidden columns
2038
2049
  this._selectedColumns = (this.columns ?? []).filter((c) => !c?.hidden);
2039
2050
  // Restore columns once when config/columns arrive
@@ -2045,7 +2056,13 @@ class TableCaptionComponent {
2045
2056
  }
2046
2057
  // Normalize filters (backward compatible) before building form
2047
2058
  const normalizedFilters = this.normalizeFilters(this.filters);
2048
- const signature = this.buildSignature(this.tableConfiguration.key, normalizedFilters);
2059
+ // Key the rebuild signature on the storage identity (FILTER_KEY), not just
2060
+ // the table key. Sibling tabs that share the same filter columns (e.g. IT
2061
+ // Systems vs Virtualization) but persist under different stateKeys must
2062
+ // rebuild — and thus re-restore — when switched between while the caption
2063
+ // instance is reused; otherwise the reused form keeps the previous tab's
2064
+ // values and the persisted filter looks lost until a full reload.
2065
+ const signature = this.buildSignature(this.FILTER_KEY, normalizedFilters);
2049
2066
  if (signature !== this.lastSignature) {
2050
2067
  this.lastSignature = signature;
2051
2068
  this.buildFiltersForm(normalizedFilters);
@@ -2082,19 +2099,34 @@ class TableCaptionComponent {
2082
2099
  this.filtersForm = fg;
2083
2100
  // 1) Restore persisted values (but only valid keys)
2084
2101
  this.restoreFilterState(filtersCfg);
2085
- // 2) Subscribe once with debounce and emit normalized payload
2086
- this.filtersForm.valueChanges
2102
+ // An empty `filtersCfg` means the filter options are not built yet (they are
2103
+ // derived from the loaded table data), NOT that the user cleared their
2104
+ // filters. Persisting here would write an empty payload and, via
2105
+ // persistState(), DELETE the saved state before the real config arrives on
2106
+ // the next build — the init-order wipe. Only persist once a real filter
2107
+ // config exists; a genuine "user cleared" is an empty value with a non-empty
2108
+ // config and still persists (and removes) correctly.
2109
+ const hasFilterConfig = filtersCfg.length > 0;
2110
+ // 2) Subscribe with debounce and emit normalized payload.
2111
+ // buildFiltersForm() runs again whenever the filter signature changes (e.g.
2112
+ // range bounds move on a data reload), so tear down the previous form's
2113
+ // subscription first — otherwise they accumulate for the component's whole
2114
+ // lifetime and emit/persist in multiples.
2115
+ this.filtersValueSub?.unsubscribe();
2116
+ this.filtersValueSub = this.filtersForm.valueChanges
2087
2117
  .pipe(takeUntilDestroyed(this.dr), debounceTime(120), map((raw) => this.normalizeOutgoingPayload(raw, filtersCfg)))
2088
2118
  .subscribe((payload) => {
2089
2119
  this.applyFiltersEvent.emit(payload);
2090
- this.persistState(payload, this.FILTER_KEY);
2120
+ if (hasFilterConfig)
2121
+ this.persistState(payload, this.FILTER_KEY);
2091
2122
  });
2092
2123
  // 3) Emit initial state (normalized) once
2093
2124
  queueMicrotask(() => {
2094
2125
  const raw = this.filtersForm.getRawValue();
2095
2126
  const payload = this.normalizeOutgoingPayload(raw, filtersCfg);
2096
2127
  this.applyFiltersEvent.emit(payload);
2097
- this.persistState(payload, this.FILTER_KEY);
2128
+ if (hasFilterConfig)
2129
+ this.persistState(payload, this.FILTER_KEY);
2098
2130
  });
2099
2131
  }
2100
2132
  /**
@@ -3276,7 +3308,14 @@ class TableComponent {
3276
3308
  }
3277
3309
  }
3278
3310
  }
3279
- this.allData = [...this.originalData];
3311
+ // Re-apply any active filters to the refreshed dataset. Previously this
3312
+ // reset to the full (unfiltered) originalData, so every SSE / reload dropped
3313
+ // the user's active filters until the caption happened to re-emit — the
3314
+ // "filters lost on reload" bug that consumers worked around app-side.
3315
+ this.allData =
3316
+ Object.keys(this.currentFilters).length > 0
3317
+ ? this.getFilteredData()
3318
+ : [...this.originalData];
3280
3319
  this.lastSortKey = '';
3281
3320
  // allow re-applying initial sort for new dataset
3282
3321
  this.initialSortApplied = false;
@@ -3301,8 +3340,16 @@ class TableComponent {
3301
3340
  if (changes['filters']) {
3302
3341
  this.enrichRangeFilters();
3303
3342
  }
3304
- if (this.tableConfiguration?.key) {
3305
- this.SORT_KEY = `sort_${this.tableConfiguration.key}`;
3343
+ // Keep the sort key on the same scheme as filters/columns (see
3344
+ // table-caption): honour `stateKey` (fallback `key`) and `stateKeyPrefix`
3345
+ // so sort state neither collides across tables sharing `key` nor leaks
3346
+ // between users on a shared machine.
3347
+ const sortBase = this.tableConfiguration?.stateKey ?? this.tableConfiguration?.key;
3348
+ if (sortBase) {
3349
+ const sortPrefix = this.tableConfiguration?.stateKeyPrefix
3350
+ ? `${this.tableConfiguration.stateKeyPrefix}.`
3351
+ : '';
3352
+ this.SORT_KEY = `sort_${sortPrefix}${sortBase}`;
3306
3353
  }
3307
3354
  // Build columnTypeMap for sorting normalization
3308
3355
  if (this.columns?.length > 0) {
@@ -8480,6 +8527,49 @@ var SimpleButtonType;
8480
8527
  SimpleButtonType["SPLIT"] = "split";
8481
8528
  })(SimpleButtonType || (SimpleButtonType = {}));
8482
8529
 
8530
+ /**
8531
+ * localStorage key prefixes the data-table persists its per-view UI state under.
8532
+ * Kept in sync with the key scheme in table-caption (filters/columns) and
8533
+ * table.component (sort): `${prefix}${stateKeyPrefix}.${stateKey ?? key}`.
8534
+ */
8535
+ const TABLE_STATE_PREFIXES = ['filters_', 'columns_', 'sort_'];
8536
+ /**
8537
+ * Remove the data-table's persisted UI state (filters, selected columns, sort)
8538
+ * from localStorage. Call this on logout so a user's saved table views do not
8539
+ * carry over into the next login on a shared machine.
8540
+ *
8541
+ * @param userPrefix When the app persists state user-scoped
8542
+ * (`tableConfiguration.stateKeyPrefix`, e.g. the user id), pass the SAME value
8543
+ * to clear only that user's state — other users' saved views on this browser
8544
+ * stay intact. Omit to clear all persisted table state on this browser.
8545
+ */
8546
+ function clearPersistedTableState(userPrefix) {
8547
+ let store;
8548
+ try {
8549
+ store = localStorage;
8550
+ }
8551
+ catch {
8552
+ return; // storage disabled / unavailable — nothing to clear
8553
+ }
8554
+ // Scoped keys look like `filters_<userPrefix>.<base>`; the trailing dot makes
8555
+ // the match exact so prefix "1" never also clears user "12"'s state.
8556
+ const scoped = userPrefix ? `${userPrefix}.` : '';
8557
+ const doomed = [];
8558
+ for (let i = 0; i < store.length; i++) {
8559
+ const key = store.key(i);
8560
+ if (!key)
8561
+ continue;
8562
+ for (const p of TABLE_STATE_PREFIXES) {
8563
+ if (key.startsWith(p + scoped)) {
8564
+ doomed.push(key);
8565
+ break;
8566
+ }
8567
+ }
8568
+ }
8569
+ for (const key of doomed)
8570
+ store.removeItem(key);
8571
+ }
8572
+
8483
8573
  class StripHtmlSafePipe {
8484
8574
  transform(value) {
8485
8575
  if (value === null || value === undefined)
@@ -11284,5 +11374,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
11284
11374
  * Generated bundle index. Do not edit.
11285
11375
  */
11286
11376
 
11287
- export { ActionTypes, ActionsComponent, CardComponent, ContexObjectComponent, ControlType, GroupsFormComponent, InnerHeaderComponent, META_FORM_ASYNC_EXECUTOR, MetaFormButtonsComponent, MetaFormButtonsV2Component, MetaFormComponent, MetaFormFieldV2Component, MetaFormGroupV2Component, MetaFormService, MetaFormV2Component, MetaSubmitValidatorService, ObjectItemDialogComponent, QuickPickComponent, QuickPickSidePanelComponent, SearchBarComponent, ShellComponent, SidebarComponent, SidebarItemComponent, SimpleButtonType, StatusBarComponent, StatusColType, StatusHeaderComponent, StatusTooltipType, TableComponent, TopbarComponent, UserComponent, tableActionType, tableButtonContext, tableButtonFormat, tableColumnType, tableFilterType, tableSelectionType };
11377
+ export { ActionTypes, ActionsComponent, CardComponent, ContexObjectComponent, ControlType, GroupsFormComponent, InnerHeaderComponent, META_FORM_ASYNC_EXECUTOR, MetaFormButtonsComponent, MetaFormButtonsV2Component, MetaFormComponent, MetaFormFieldV2Component, MetaFormGroupV2Component, MetaFormService, MetaFormV2Component, MetaSubmitValidatorService, ObjectItemDialogComponent, QuickPickComponent, QuickPickSidePanelComponent, SearchBarComponent, ShellComponent, SidebarComponent, SidebarItemComponent, SimpleButtonType, StatusBarComponent, StatusColType, StatusHeaderComponent, StatusTooltipType, TableComponent, TopbarComponent, UserComponent, clearPersistedTableState, tableActionType, tableButtonContext, tableButtonFormat, tableColumnType, tableFilterType, tableSelectionType };
11288
11378
  //# sourceMappingURL=fuentis-phoenix-ui.mjs.map