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

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.
@@ -2023,7 +2023,8 @@ class TableCaptionComponent {
2023
2023
  */
2024
2024
  lastSignature = '';
2025
2025
  ngOnChanges(changes) {
2026
- if (changes['filterValidValues'] && !changes['filterValidValues'].firstChange) {
2026
+ if (changes['filterValidValues'] &&
2027
+ !changes['filterValidValues'].firstChange) {
2027
2028
  const validMap = changes['filterValidValues'].currentValue;
2028
2029
  if (validMap)
2029
2030
  this.applyValidValuesPatch(validMap);
@@ -2075,7 +2076,7 @@ class TableCaptionComponent {
2075
2076
  ? !!f.selected
2076
2077
  : t === 'range'
2077
2078
  ? this.normalizeRangeInitial(f.selected, f)
2078
- : f.selected ?? '';
2079
+ : (f.selected ?? '');
2079
2080
  fg.addControl(f.key, this.fb.control(initialValue));
2080
2081
  }
2081
2082
  this.filtersForm = fg;
@@ -2263,7 +2264,9 @@ class TableCaptionComponent {
2263
2264
  continue;
2264
2265
  }
2265
2266
  if (t === 'multiselect') {
2266
- const arr = Array.isArray(v) ? v.filter((x) => x != null && x !== '') : [];
2267
+ const arr = Array.isArray(v)
2268
+ ? v.filter((x) => x != null && x !== '')
2269
+ : [];
2267
2270
  if (arr.length)
2268
2271
  out[key] = arr;
2269
2272
  continue;
@@ -2293,6 +2296,11 @@ class TableCaptionComponent {
2293
2296
  * NOTE:
2294
2297
  * - boolean-multiselect is restored into OPTION OBJECTS (for UI),
2295
2298
  * even though persisted payload is boolean[].
2299
+ * - Since `options` are rebuilt from the currently loaded table data (see
2300
+ * createDynamicFilters), a value selected in one context (e.g. a different
2301
+ * Entity in "Add link") may no longer exist in the current options. We
2302
+ * revalidate multiselect/dropdown selections against the current options
2303
+ * on every restore, so stale values don't render as broken/empty entries.
2296
2304
  */
2297
2305
  restoreFilterState(cfg) {
2298
2306
  const stored = localStorage.getItem(this.FILTER_KEY);
@@ -2308,25 +2316,39 @@ class TableCaptionComponent {
2308
2316
  if (known.has(k))
2309
2317
  patch[k] = parsed[k];
2310
2318
  }
2319
+ let changed = false;
2311
2320
  for (const f of cfg) {
2312
2321
  const t = f.type ?? 'text';
2322
+ const options = Array.isArray(f.options) ? f.options : [];
2323
+ const ov = f.optionValue ?? 'key';
2313
2324
  if (t === 'boolean-multiselect') {
2314
- // parsed payload is boolean[]; map -> option objects
2325
+ // parsed payload is boolean[]; map -> option objects (already validated against options)
2315
2326
  patch[f.key] = this.normalizeBooleanMultiSelection(patch[f.key], f);
2316
2327
  continue;
2317
2328
  }
2318
2329
  if (t === 'multiselect') {
2319
2330
  const val = patch[f.key];
2320
- if (val == null)
2321
- patch[f.key] = [];
2322
- else if (!Array.isArray(val))
2323
- patch[f.key] = [val];
2331
+ const arr = val == null ? [] : Array.isArray(val) ? val : [val];
2332
+ const validValues = new Set(options.map((o) => o?.[ov]));
2333
+ const filtered = arr.filter((x) => validValues.has(x));
2334
+ if (filtered.length !== arr.length)
2335
+ changed = true;
2336
+ patch[f.key] = filtered;
2324
2337
  continue;
2325
2338
  }
2326
2339
  if (t === 'checkbox') {
2327
2340
  patch[f.key] = !!patch[f.key];
2328
2341
  continue;
2329
2342
  }
2343
+ if (t === 'dropdown' || t === 'person-dropdown') {
2344
+ const val = patch[f.key];
2345
+ const isEmpty = val == null || val === '';
2346
+ const isValid = isEmpty || options.some((o) => o?.[ov] === val);
2347
+ if (!isValid) {
2348
+ patch[f.key] = '';
2349
+ changed = true;
2350
+ }
2351
+ }
2330
2352
  if (t === 'range') {
2331
2353
  // persisted payload is { type:'range', min, max }; map -> [from, to]
2332
2354
  const stored = patch[f.key];
@@ -2340,6 +2362,11 @@ class TableCaptionComponent {
2340
2362
  }
2341
2363
  }
2342
2364
  this.filtersForm.patchValue(patch, { emitEvent: false });
2365
+ // Persist the pruned state so stale (non-existent) values don't linger in storage.
2366
+ if (changed) {
2367
+ const payload = this.normalizeOutgoingPayload(this.filtersForm.getRawValue(), cfg);
2368
+ this.persistState(payload, this.FILTER_KEY);
2369
+ }
2343
2370
  }
2344
2371
  catch {
2345
2372
  // ignore invalid state
@@ -2433,7 +2460,9 @@ class TableCaptionComponent {
2433
2460
  /** Reset a range filter back to its full [min, max] bounds. */
2434
2461
  resetRangeFilter(control) {
2435
2462
  const [min, max] = this.rangeBounds(control);
2436
- this.filtersForm.get(control.key)?.setValue([min, max], { emitEvent: true });
2463
+ this.filtersForm
2464
+ .get(control.key)
2465
+ ?.setValue([min, max], { emitEvent: true });
2437
2466
  }
2438
2467
  resetFilters() {
2439
2468
  this.filtersForm.reset({}, { emitEvent: true });
@@ -2443,7 +2472,9 @@ class TableCaptionComponent {
2443
2472
  persistState(state, key) {
2444
2473
  const isEmpty = state == null ||
2445
2474
  (Array.isArray(state) && state.length === 0) ||
2446
- (typeof state === 'object' && !Array.isArray(state) && Object.keys(state).length === 0);
2475
+ (typeof state === 'object' &&
2476
+ !Array.isArray(state) &&
2477
+ Object.keys(state).length === 0);
2447
2478
  if (isEmpty)
2448
2479
  localStorage.removeItem(key);
2449
2480
  else
@@ -2456,7 +2487,7 @@ class TableCaptionComponent {
2456
2487
  const arr = Array.isArray(value) ? value : [];
2457
2488
  const labelField = this.getOptionLabelField(control);
2458
2489
  return arr
2459
- .map((x) => (x && typeof x === 'object' ? x[labelField] : String(x ?? '')))
2490
+ .map((x) => x && typeof x === 'object' ? x[labelField] : String(x ?? ''))
2460
2491
  .filter((s) => !!s)
2461
2492
  .join(', ');
2462
2493
  }