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

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.
@@ -2056,7 +2056,13 @@ class TableCaptionComponent {
2056
2056
  }
2057
2057
  // Normalize filters (backward compatible) before building form
2058
2058
  const normalizedFilters = this.normalizeFilters(this.filters);
2059
- 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);
2060
2066
  if (signature !== this.lastSignature) {
2061
2067
  this.lastSignature = signature;
2062
2068
  this.buildFiltersForm(normalizedFilters);
@@ -2119,8 +2125,9 @@ class TableCaptionComponent {
2119
2125
  const raw = this.filtersForm.getRawValue();
2120
2126
  const payload = this.normalizeOutgoingPayload(raw, filtersCfg);
2121
2127
  this.applyFiltersEvent.emit(payload);
2128
+ // Initial/restore emit — save if there's something, but never DELETE here.
2122
2129
  if (hasFilterConfig)
2123
- this.persistState(payload, this.FILTER_KEY);
2130
+ this.persistState(payload, this.FILTER_KEY, false);
2124
2131
  });
2125
2132
  }
2126
2133
  /**
@@ -2147,11 +2154,19 @@ class TableCaptionComponent {
2147
2154
  }
2148
2155
  buildSignature(tableKey, filtersCfg) {
2149
2156
  const keys = (filtersCfg ?? [])
2150
- .map((f) =>
2151
- // Range bounds are data-derived and can change without the key set
2152
- // changing; fold them into the signature so the form rebuilds when
2153
- // the min/max move.
2154
- f.type === 'range' ? `${f.key}:${f.min}-${f.max}` : f.key)
2157
+ .map((f) => {
2158
+ // Range bounds are data-derived and can change without the key set
2159
+ // changing; fold them into the signature so the form rebuilds when
2160
+ // the min/max move.
2161
+ if (f.type === 'range')
2162
+ return `${f.key}:${f.min}-${f.max}`;
2163
+ // Options arrive after the data loads. Fold their presence in so the
2164
+ // form rebuilds — and re-restores against real options — once they're
2165
+ // available, instead of staying with the value dropped on the first
2166
+ // (option-less) build.
2167
+ const hasOptions = Array.isArray(f.options) && f.options.length > 0;
2168
+ return `${f.key}${hasOptions ? ':o' : ''}`;
2169
+ })
2155
2170
  .join('|');
2156
2171
  return `${tableKey ?? 'table'}::${keys}`;
2157
2172
  }
@@ -2176,7 +2191,8 @@ class TableCaptionComponent {
2176
2191
  if (Object.keys(patch).length > 0) {
2177
2192
  this.filtersForm.patchValue(patch, { emitEvent: true });
2178
2193
  const payload = this.normalizeOutgoingPayload(this.filtersForm.getRawValue(), cfg);
2179
- this.persistState(payload, this.FILTER_KEY);
2194
+ // Programmatic valid-values sync — update, but never delete.
2195
+ this.persistState(payload, this.FILTER_KEY, false);
2180
2196
  }
2181
2197
  }
2182
2198
  normalizeMultiInitial(selected) {
@@ -2388,10 +2404,11 @@ class TableCaptionComponent {
2388
2404
  }
2389
2405
  }
2390
2406
  this.filtersForm.patchValue(patch, { emitEvent: false });
2391
- // Persist the pruned state so stale (non-existent) values don't linger in storage.
2407
+ // Persist the pruned state so stale (non-existent) values don't linger in
2408
+ // storage — but never DELETE here (empty after pruning ≠ user cleared).
2392
2409
  if (changed) {
2393
2410
  const payload = this.normalizeOutgoingPayload(this.filtersForm.getRawValue(), cfg);
2394
- this.persistState(payload, this.FILTER_KEY);
2411
+ this.persistState(payload, this.FILTER_KEY, false);
2395
2412
  }
2396
2413
  }
2397
2414
  catch {
@@ -2495,16 +2512,23 @@ class TableCaptionComponent {
2495
2512
  localStorage.removeItem(this.FILTER_KEY);
2496
2513
  this.filtersPopover?.hide();
2497
2514
  }
2498
- persistState(state, key) {
2515
+ persistState(state, key, allowDelete = true) {
2499
2516
  const isEmpty = state == null ||
2500
2517
  (Array.isArray(state) && state.length === 0) ||
2501
2518
  (typeof state === 'object' &&
2502
2519
  !Array.isArray(state) &&
2503
2520
  Object.keys(state).length === 0);
2504
- if (isEmpty)
2505
- localStorage.removeItem(key);
2506
- else
2507
- localStorage.setItem(key, JSON.stringify(state));
2521
+ if (isEmpty) {
2522
+ // Only a USER-initiated clear (valueChanges) may delete a saved filter.
2523
+ // Programmatic emits (initial rebuild, valid-values pruning, restore
2524
+ // pruning) pass allowDelete=false, so a transient empty form — e.g. a
2525
+ // caption rebuilt before its options loaded, which drops the restored
2526
+ // value — never wipes a perfectly good persisted filter.
2527
+ if (allowDelete)
2528
+ localStorage.removeItem(key);
2529
+ return;
2530
+ }
2531
+ localStorage.setItem(key, JSON.stringify(state));
2508
2532
  }
2509
2533
  getOptionLabelField(control) {
2510
2534
  return (control?.optionLabel ?? 'label');