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

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
@@ -2082,19 +2093,34 @@ class TableCaptionComponent {
2082
2093
  this.filtersForm = fg;
2083
2094
  // 1) Restore persisted values (but only valid keys)
2084
2095
  this.restoreFilterState(filtersCfg);
2085
- // 2) Subscribe once with debounce and emit normalized payload
2086
- this.filtersForm.valueChanges
2096
+ // An empty `filtersCfg` means the filter options are not built yet (they are
2097
+ // derived from the loaded table data), NOT that the user cleared their
2098
+ // filters. Persisting here would write an empty payload and, via
2099
+ // persistState(), DELETE the saved state before the real config arrives on
2100
+ // the next build — the init-order wipe. Only persist once a real filter
2101
+ // config exists; a genuine "user cleared" is an empty value with a non-empty
2102
+ // config and still persists (and removes) correctly.
2103
+ const hasFilterConfig = filtersCfg.length > 0;
2104
+ // 2) Subscribe with debounce and emit normalized payload.
2105
+ // buildFiltersForm() runs again whenever the filter signature changes (e.g.
2106
+ // range bounds move on a data reload), so tear down the previous form's
2107
+ // subscription first — otherwise they accumulate for the component's whole
2108
+ // lifetime and emit/persist in multiples.
2109
+ this.filtersValueSub?.unsubscribe();
2110
+ this.filtersValueSub = this.filtersForm.valueChanges
2087
2111
  .pipe(takeUntilDestroyed(this.dr), debounceTime(120), map((raw) => this.normalizeOutgoingPayload(raw, filtersCfg)))
2088
2112
  .subscribe((payload) => {
2089
2113
  this.applyFiltersEvent.emit(payload);
2090
- this.persistState(payload, this.FILTER_KEY);
2114
+ if (hasFilterConfig)
2115
+ this.persistState(payload, this.FILTER_KEY);
2091
2116
  });
2092
2117
  // 3) Emit initial state (normalized) once
2093
2118
  queueMicrotask(() => {
2094
2119
  const raw = this.filtersForm.getRawValue();
2095
2120
  const payload = this.normalizeOutgoingPayload(raw, filtersCfg);
2096
2121
  this.applyFiltersEvent.emit(payload);
2097
- this.persistState(payload, this.FILTER_KEY);
2122
+ if (hasFilterConfig)
2123
+ this.persistState(payload, this.FILTER_KEY);
2098
2124
  });
2099
2125
  }
2100
2126
  /**
@@ -3276,7 +3302,14 @@ class TableComponent {
3276
3302
  }
3277
3303
  }
3278
3304
  }
3279
- this.allData = [...this.originalData];
3305
+ // Re-apply any active filters to the refreshed dataset. Previously this
3306
+ // reset to the full (unfiltered) originalData, so every SSE / reload dropped
3307
+ // the user's active filters until the caption happened to re-emit — the
3308
+ // "filters lost on reload" bug that consumers worked around app-side.
3309
+ this.allData =
3310
+ Object.keys(this.currentFilters).length > 0
3311
+ ? this.getFilteredData()
3312
+ : [...this.originalData];
3280
3313
  this.lastSortKey = '';
3281
3314
  // allow re-applying initial sort for new dataset
3282
3315
  this.initialSortApplied = false;
@@ -3301,8 +3334,16 @@ class TableComponent {
3301
3334
  if (changes['filters']) {
3302
3335
  this.enrichRangeFilters();
3303
3336
  }
3304
- if (this.tableConfiguration?.key) {
3305
- this.SORT_KEY = `sort_${this.tableConfiguration.key}`;
3337
+ // Keep the sort key on the same scheme as filters/columns (see
3338
+ // table-caption): honour `stateKey` (fallback `key`) and `stateKeyPrefix`
3339
+ // so sort state neither collides across tables sharing `key` nor leaks
3340
+ // between users on a shared machine.
3341
+ const sortBase = this.tableConfiguration?.stateKey ?? this.tableConfiguration?.key;
3342
+ if (sortBase) {
3343
+ const sortPrefix = this.tableConfiguration?.stateKeyPrefix
3344
+ ? `${this.tableConfiguration.stateKeyPrefix}.`
3345
+ : '';
3346
+ this.SORT_KEY = `sort_${sortPrefix}${sortBase}`;
3306
3347
  }
3307
3348
  // Build columnTypeMap for sorting normalization
3308
3349
  if (this.columns?.length > 0) {
@@ -8480,6 +8521,49 @@ var SimpleButtonType;
8480
8521
  SimpleButtonType["SPLIT"] = "split";
8481
8522
  })(SimpleButtonType || (SimpleButtonType = {}));
8482
8523
 
8524
+ /**
8525
+ * localStorage key prefixes the data-table persists its per-view UI state under.
8526
+ * Kept in sync with the key scheme in table-caption (filters/columns) and
8527
+ * table.component (sort): `${prefix}${stateKeyPrefix}.${stateKey ?? key}`.
8528
+ */
8529
+ const TABLE_STATE_PREFIXES = ['filters_', 'columns_', 'sort_'];
8530
+ /**
8531
+ * Remove the data-table's persisted UI state (filters, selected columns, sort)
8532
+ * from localStorage. Call this on logout so a user's saved table views do not
8533
+ * carry over into the next login on a shared machine.
8534
+ *
8535
+ * @param userPrefix When the app persists state user-scoped
8536
+ * (`tableConfiguration.stateKeyPrefix`, e.g. the user id), pass the SAME value
8537
+ * to clear only that user's state — other users' saved views on this browser
8538
+ * stay intact. Omit to clear all persisted table state on this browser.
8539
+ */
8540
+ function clearPersistedTableState(userPrefix) {
8541
+ let store;
8542
+ try {
8543
+ store = localStorage;
8544
+ }
8545
+ catch {
8546
+ return; // storage disabled / unavailable — nothing to clear
8547
+ }
8548
+ // Scoped keys look like `filters_<userPrefix>.<base>`; the trailing dot makes
8549
+ // the match exact so prefix "1" never also clears user "12"'s state.
8550
+ const scoped = userPrefix ? `${userPrefix}.` : '';
8551
+ const doomed = [];
8552
+ for (let i = 0; i < store.length; i++) {
8553
+ const key = store.key(i);
8554
+ if (!key)
8555
+ continue;
8556
+ for (const p of TABLE_STATE_PREFIXES) {
8557
+ if (key.startsWith(p + scoped)) {
8558
+ doomed.push(key);
8559
+ break;
8560
+ }
8561
+ }
8562
+ }
8563
+ for (const key of doomed)
8564
+ store.removeItem(key);
8565
+ }
8566
+
8483
8567
  class StripHtmlSafePipe {
8484
8568
  transform(value) {
8485
8569
  if (value === null || value === undefined)
@@ -11284,5 +11368,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
11284
11368
  * Generated bundle index. Do not edit.
11285
11369
  */
11286
11370
 
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 };
11371
+ 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
11372
  //# sourceMappingURL=fuentis-phoenix-ui.mjs.map