@fuentis/phoenix-ui 0.0.9-alpha.639 → 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.
@@ -2125,8 +2125,9 @@ class TableCaptionComponent {
2125
2125
  const raw = this.filtersForm.getRawValue();
2126
2126
  const payload = this.normalizeOutgoingPayload(raw, filtersCfg);
2127
2127
  this.applyFiltersEvent.emit(payload);
2128
+ // Initial/restore emit — save if there's something, but never DELETE here.
2128
2129
  if (hasFilterConfig)
2129
- this.persistState(payload, this.FILTER_KEY);
2130
+ this.persistState(payload, this.FILTER_KEY, false);
2130
2131
  });
2131
2132
  }
2132
2133
  /**
@@ -2153,11 +2154,19 @@ class TableCaptionComponent {
2153
2154
  }
2154
2155
  buildSignature(tableKey, filtersCfg) {
2155
2156
  const keys = (filtersCfg ?? [])
2156
- .map((f) =>
2157
- // Range bounds are data-derived and can change without the key set
2158
- // changing; fold them into the signature so the form rebuilds when
2159
- // the min/max move.
2160
- 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
+ })
2161
2170
  .join('|');
2162
2171
  return `${tableKey ?? 'table'}::${keys}`;
2163
2172
  }
@@ -2182,7 +2191,8 @@ class TableCaptionComponent {
2182
2191
  if (Object.keys(patch).length > 0) {
2183
2192
  this.filtersForm.patchValue(patch, { emitEvent: true });
2184
2193
  const payload = this.normalizeOutgoingPayload(this.filtersForm.getRawValue(), cfg);
2185
- this.persistState(payload, this.FILTER_KEY);
2194
+ // Programmatic valid-values sync — update, but never delete.
2195
+ this.persistState(payload, this.FILTER_KEY, false);
2186
2196
  }
2187
2197
  }
2188
2198
  normalizeMultiInitial(selected) {
@@ -2394,10 +2404,11 @@ class TableCaptionComponent {
2394
2404
  }
2395
2405
  }
2396
2406
  this.filtersForm.patchValue(patch, { emitEvent: false });
2397
- // 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).
2398
2409
  if (changed) {
2399
2410
  const payload = this.normalizeOutgoingPayload(this.filtersForm.getRawValue(), cfg);
2400
- this.persistState(payload, this.FILTER_KEY);
2411
+ this.persistState(payload, this.FILTER_KEY, false);
2401
2412
  }
2402
2413
  }
2403
2414
  catch {
@@ -2501,16 +2512,23 @@ class TableCaptionComponent {
2501
2512
  localStorage.removeItem(this.FILTER_KEY);
2502
2513
  this.filtersPopover?.hide();
2503
2514
  }
2504
- persistState(state, key) {
2515
+ persistState(state, key, allowDelete = true) {
2505
2516
  const isEmpty = state == null ||
2506
2517
  (Array.isArray(state) && state.length === 0) ||
2507
2518
  (typeof state === 'object' &&
2508
2519
  !Array.isArray(state) &&
2509
2520
  Object.keys(state).length === 0);
2510
- if (isEmpty)
2511
- localStorage.removeItem(key);
2512
- else
2513
- 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));
2514
2532
  }
2515
2533
  getOptionLabelField(control) {
2516
2534
  return (control?.optionLabel ?? 'label');