@dereekb/rxjs 14.8.0 → 14.10.0

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.
package/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
- import { isWebsiteUrlWithPrefix, getValueFromGetter, isMaybeSo, filterMaybeArrayValues, areEqualPOJOValues, convertToArray, asGetter, performTaskLoop, isMaybeNot, forEachWithArray, pushArrayItemsIntoArray, asArray, pushItemOrArrayItemsIntoArray, filterAndMapFunction, expirationDetails, objectKeysEqualityComparatorFunction, objectKeyEqualityComparatorFunction, asPromise, randomNumberFactory, mapsHaveSameKeys, mapKeysIntersectionObjectToArray, incrementingNumberFactory, filterUniqueFunction, build, cachedGetter, allKeyValueTuples, keyValueMapFactory, multiKeyValueMapFactory, timePeriodCounter, hasSameValues, compareEqualityWithValueFromItemsFunction, setContainsAllValues, setContainsAnyValue, setContainsNoneOfValue, searchStringFilterFunction, reduceBooleansWithAnd, toReadableError, reduceBooleansWithOr, valuesAreBothNullishOrEquivalent, mergeObjects, objectHasKey, safeCompareEquality, hasNonNullValue, limitArray, mapFunctionOutputPair, lastValue, flattenArray, hasValueOrNotEmpty, filteredPage, invertMaybeBoolean, FIRST_PAGE, getNextPageNumber, reduceBooleansWithOrFn, MS_IN_SECOND } from '@dereekb/util';
2
- import { defer, from, isObservable, of, switchMap, combineLatest, map, delay, startWith, filter, skipWhile, EMPTY, distinctUntilChanged, mergeMap, BehaviorSubject, shareReplay, skip, defaultIfEmpty, first, finalize, merge, firstValueFrom, exhaustMap, scan, identity, throttleTime, takeWhile, timeout, tap, throwError, timer, delayWhen, asyncScheduler, catchError, Subject } from 'rxjs';
1
+ import { isWebsiteUrlWithPrefix, getValueFromGetter, isMaybeSo, filterMaybeArrayValues, areEqualPOJOValues, convertToArray, mergeObjects, asGetter, performTaskLoop, isMaybeNot, forEachWithArray, pushArrayItemsIntoArray, asArray, pushItemOrArrayItemsIntoArray, filterAndMapFunction, expirationDetails, objectKeysEqualityComparatorFunction, objectKeyEqualityComparatorFunction, asPromise, randomNumberFactory, mapsHaveSameKeys, mapKeysIntersectionObjectToArray, incrementingNumberFactory, filterUniqueFunction, build, cachedGetter, allKeyValueTuples, keyValueMapFactory, multiKeyValueMapFactory, timePeriodCounter, hasSameValues, compareEqualityWithValueFromItemsFunction, setContainsAllValues, setContainsAnyValue, setContainsNoneOfValue, searchStringFilterFunction, reduceBooleansWithAnd, toReadableError, reduceBooleansWithOr, valuesAreBothNullishOrEquivalent, objectHasKey, safeCompareEquality, hasNonNullValue, limitArray, mapFunctionOutputPair, lastValue, flattenArray, hasValueOrNotEmpty, filteredPage, invertMaybeBoolean, FIRST_PAGE, getNextPageNumber, reduceBooleansWithOrFn, MS_IN_SECOND } from '@dereekb/util';
2
+ import { defer, from, isObservable, of, switchMap, combineLatest, map, delay, startWith, filter, skipWhile, EMPTY, mergeMap, shareReplay, BehaviorSubject, finalize, Observable, distinctUntilChanged, skip, defaultIfEmpty, first, merge, concat, firstValueFrom, exhaustMap, scan, identity, throttleTime, takeWhile, timeout, tap, throwError, timer, delayWhen, asyncScheduler, catchError, Subject } from 'rxjs';
3
3
 
4
4
  function _class_call_check$9(instance, Constructor) {
5
5
  if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
@@ -950,6 +950,157 @@ function switchMapOnBoolean(switchOnValue, obs, otherwise) {
950
950
  };
951
951
  }
952
952
 
953
+ function _array_like_to_array$9(arr, len) {
954
+ if (len == null || len > arr.length) len = arr.length;
955
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
956
+ return arr2;
957
+ }
958
+ function _array_with_holes$4(arr) {
959
+ if (Array.isArray(arr)) return arr;
960
+ }
961
+ function _iterable_to_array_limit$4(arr, i) {
962
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
963
+ if (_i == null) return;
964
+ var _arr = [];
965
+ var _n = true;
966
+ var _d = false;
967
+ var _s, _e;
968
+ try {
969
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
970
+ _arr.push(_s.value);
971
+ if (i && _arr.length === i) break;
972
+ }
973
+ } catch (err) {
974
+ _d = true;
975
+ _e = err;
976
+ } finally{
977
+ try {
978
+ if (!_n && _i["return"] != null) _i["return"]();
979
+ } finally{
980
+ if (_d) throw _e;
981
+ }
982
+ }
983
+ return _arr;
984
+ }
985
+ function _non_iterable_rest$4() {
986
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
987
+ }
988
+ function _sliced_to_array$4(arr, i) {
989
+ return _array_with_holes$4(arr) || _iterable_to_array_limit$4(arr, i) || _unsupported_iterable_to_array$9(arr, i) || _non_iterable_rest$4();
990
+ }
991
+ function _unsupported_iterable_to_array$9(o, minLen) {
992
+ if (!o) return;
993
+ if (typeof o === "string") return _array_like_to_array$9(o, minLen);
994
+ var n = Object.prototype.toString.call(o).slice(8, -1);
995
+ if (n === "Object" && o.constructor) n = o.constructor.name;
996
+ if (n === "Map" || n === "Set") return Array.from(n);
997
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$9(o, minLen);
998
+ }
999
+ /**
1000
+ * Combines `startWith` and {@link tapFirst} to initialize an observable pipe with a side-effect on the first emission.
1001
+ *
1002
+ * Emits the `initial` value first, then taps on the first emitted value to run the provided callback.
1003
+ *
1004
+ * @param tap - Side-effect function called with the first value.
1005
+ * @param initial - Optional starting value emitted before the source.
1006
+ * @param skipFirst - If true, skips tapping the initial value.
1007
+ * @returns An operator that initializes the pipe with a tap.
1008
+ */ function initialize(tap, initial, skipFirst) {
1009
+ return function(source) {
1010
+ var subscriber = source.pipe(startWith(initial), tapFirst(function(x) {
1011
+ return tap(x);
1012
+ }, initial == null || skipFirst));
1013
+ return subscriber;
1014
+ };
1015
+ }
1016
+ /**
1017
+ * Executes a side-effect on the first emission from the observable, then passes all values through.
1018
+ *
1019
+ * @param tap - The side-effect function to call once.
1020
+ * @param skipFirst - If true, skips the very first emission before tapping.
1021
+ * @returns An operator that taps the first value.
1022
+ */ function tapFirst(tap) {
1023
+ var skipFirst = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false;
1024
+ return skipWhile(function(value) {
1025
+ var i = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 0;
1026
+ tap(value);
1027
+ return i === 0 && !skipFirst;
1028
+ });
1029
+ }
1030
+ /**
1031
+ * RxJS operator that ignores the values the source emits synchronously while it is being subscribed to, such as the
1032
+ * current value of a BehaviorSubject or the latest value replayed by `shareReplay()`. Only values emitted afterwards pass through.
1033
+ *
1034
+ * @returns Operator that skips the values replayed on subscription.
1035
+ *
1036
+ * @example
1037
+ * ```ts
1038
+ * const value = new BehaviorSubject(1);
1039
+ * value.pipe(skipReplayedValues()).subscribe(console.log);
1040
+ * value.next(2);
1041
+ * // Output: 2
1042
+ * ```
1043
+ */ function skipReplayedValues() {
1044
+ return function(source) {
1045
+ return new Observable(function(subscriber) {
1046
+ var subscribing = true;
1047
+ var subscription = source.subscribe({
1048
+ next: function next(x) {
1049
+ if (!subscribing) {
1050
+ subscriber.next(x);
1051
+ }
1052
+ },
1053
+ error: function error(e) {
1054
+ return subscriber.error(e);
1055
+ },
1056
+ complete: function complete() {
1057
+ return subscriber.complete();
1058
+ }
1059
+ });
1060
+ subscribing = false;
1061
+ return subscription;
1062
+ });
1063
+ };
1064
+ }
1065
+ /**
1066
+ * Wraps an observable so that it never emits `complete` until it is unsubscribed.
1067
+ *
1068
+ * The subscription will never have `complete()` called since it only triggers after unsubscription.
1069
+ * Use `finalize()` for additional cleanup.
1070
+ *
1071
+ * @param obs - The source observable to wrap.
1072
+ * @returns An observable that only completes on unsubscription.
1073
+ */ function preventComplete(obs) {
1074
+ var complete = new BehaviorSubject(0);
1075
+ return combineLatest([
1076
+ obs,
1077
+ complete
1078
+ ]).pipe(map(function(param) {
1079
+ var _param = _sliced_to_array$4(param, 1), x = _param[0];
1080
+ return x;
1081
+ }), shareReplay(1), finalize(function() {
1082
+ complete.complete();
1083
+ }));
1084
+ }
1085
+ /**
1086
+ * Creates a cold observable that defers execution of the getter until subscription, then shares the result.
1087
+ *
1088
+ * Unlike `from()`, the promise/observable is not created until the first subscriber connects.
1089
+ *
1090
+ * @param getter - Factory that returns a Promise or Observable.
1091
+ * @returns A shared observable that defers execution until subscription.
1092
+ *
1093
+ * @example
1094
+ * ```ts
1095
+ * const data$ = lazyFrom(() => fetch('/api/data').then(r => r.json()));
1096
+ * // The fetch is not called until data$ is subscribed to
1097
+ * ```
1098
+ */ function lazyFrom(getter) {
1099
+ return of(undefined).pipe(mergeMap(function() {
1100
+ return from(getter());
1101
+ }), shareReplay(1));
1102
+ }
1103
+
953
1104
  /**
954
1105
  * RxJS operator that suppresses consecutive emissions when the emitted POJO values are deeply equal.
955
1106
  *
@@ -979,13 +1130,13 @@ function filterIfObjectValuesUnchanged(input) {
979
1130
  return result;
980
1131
  }
981
1132
 
982
- function _array_like_to_array$9(arr, len) {
1133
+ function _array_like_to_array$8(arr, len) {
983
1134
  if (len == null || len > arr.length) len = arr.length;
984
1135
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
985
1136
  return arr2;
986
1137
  }
987
1138
  function _array_without_holes$4(arr) {
988
- if (Array.isArray(arr)) return _array_like_to_array$9(arr);
1139
+ if (Array.isArray(arr)) return _array_like_to_array$8(arr);
989
1140
  }
990
1141
  function _class_call_check$8(instance, Constructor) {
991
1142
  if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
@@ -1023,15 +1174,15 @@ function _non_iterable_spread$4() {
1023
1174
  throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
1024
1175
  }
1025
1176
  function _to_consumable_array$4(arr) {
1026
- return _array_without_holes$4(arr) || _iterable_to_array$4(arr) || _unsupported_iterable_to_array$9(arr) || _non_iterable_spread$4();
1177
+ return _array_without_holes$4(arr) || _iterable_to_array$4(arr) || _unsupported_iterable_to_array$8(arr) || _non_iterable_spread$4();
1027
1178
  }
1028
- function _unsupported_iterable_to_array$9(o, minLen) {
1179
+ function _unsupported_iterable_to_array$8(o, minLen) {
1029
1180
  if (!o) return;
1030
- if (typeof o === "string") return _array_like_to_array$9(o, minLen);
1181
+ if (typeof o === "string") return _array_like_to_array$8(o, minLen);
1031
1182
  var n = Object.prototype.toString.call(o).slice(8, -1);
1032
1183
  if (n === "Object" && o.constructor) n = o.constructor.name;
1033
1184
  if (n === "Map" || n === "Set") return Array.from(n);
1034
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$9(o, minLen);
1185
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$8(o, minLen);
1035
1186
  }
1036
1187
  /**
1037
1188
  * Manages a single RxJS subscription with automatic cleanup on reassignment.
@@ -1213,12 +1364,12 @@ function _unsupported_iterable_to_array$9(o, minLen) {
1213
1364
  }
1214
1365
  ();
1215
1366
 
1216
- function _array_like_to_array$8(arr, len) {
1367
+ function _array_like_to_array$7(arr, len) {
1217
1368
  if (len == null || len > arr.length) len = arr.length;
1218
1369
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
1219
1370
  return arr2;
1220
1371
  }
1221
- function _array_with_holes$4(arr) {
1372
+ function _array_with_holes$3(arr) {
1222
1373
  if (Array.isArray(arr)) return arr;
1223
1374
  }
1224
1375
  function _class_call_check$7(instance, Constructor) {
@@ -1248,7 +1399,7 @@ function _define_property$c(obj, key, value) {
1248
1399
  } else obj[key] = value;
1249
1400
  return obj;
1250
1401
  }
1251
- function _iterable_to_array_limit$4(arr, i) {
1402
+ function _iterable_to_array_limit$3(arr, i) {
1252
1403
  var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
1253
1404
  if (_i == null) return;
1254
1405
  var _arr = [];
@@ -1272,19 +1423,19 @@ function _iterable_to_array_limit$4(arr, i) {
1272
1423
  }
1273
1424
  return _arr;
1274
1425
  }
1275
- function _non_iterable_rest$4() {
1426
+ function _non_iterable_rest$3() {
1276
1427
  throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
1277
1428
  }
1278
- function _sliced_to_array$4(arr, i) {
1279
- return _array_with_holes$4(arr) || _iterable_to_array_limit$4(arr, i) || _unsupported_iterable_to_array$8(arr, i) || _non_iterable_rest$4();
1429
+ function _sliced_to_array$3(arr, i) {
1430
+ return _array_with_holes$3(arr) || _iterable_to_array_limit$3(arr, i) || _unsupported_iterable_to_array$7(arr, i) || _non_iterable_rest$3();
1280
1431
  }
1281
- function _unsupported_iterable_to_array$8(o, minLen) {
1432
+ function _unsupported_iterable_to_array$7(o, minLen) {
1282
1433
  if (!o) return;
1283
- if (typeof o === "string") return _array_like_to_array$8(o, minLen);
1434
+ if (typeof o === "string") return _array_like_to_array$7(o, minLen);
1284
1435
  var n = Object.prototype.toString.call(o).slice(8, -1);
1285
1436
  if (n === "Object" && o.constructor) n = o.constructor.name;
1286
1437
  if (n === "Map" || n === "Set") return Array.from(n);
1287
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$8(o, minLen);
1438
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$7(o, minLen);
1288
1439
  }
1289
1440
  /**
1290
1441
  * Concrete implementation of {@link FilterSource} that manages reactive filter state with
@@ -1328,7 +1479,7 @@ function _unsupported_iterable_to_array$8(o, minLen) {
1328
1479
  this._initialFilter,
1329
1480
  this._defaultFilter
1330
1481
  ]).pipe(map(function(param) {
1331
- var _param = _sliced_to_array$4(param, 2), a = _param[0], b = _param[1];
1482
+ var _param = _sliced_to_array$3(param, 2), a = _param[0], b = _param[1];
1332
1483
  return a !== null && a !== void 0 ? a : b;
1333
1484
  }), switchMapFilterMaybe(), distinctUntilChanged(), shareReplay(1)));
1334
1485
  /**
@@ -1445,13 +1596,56 @@ function _unsupported_iterable_to_array$8(o, minLen) {
1445
1596
  }
1446
1597
  ();
1447
1598
 
1448
- function _array_like_to_array$7(arr, len) {
1599
+ /**
1600
+ * Merges several partial filters into a single filter.
1601
+ *
1602
+ * Filters are applied left-to-right, so the last filter wins when two filters set the same field.
1603
+ * `undefined` values never overwrite a previously set value.
1604
+ *
1605
+ * The `preset` field is removed from the result, since a merged filter cannot represent more than one preset.
1606
+ * Each source filter keeps its own preset, so any preset menu bound to a single source still shows its selection.
1607
+ *
1608
+ * @param filters - Partial filters (or null/undefined) to merge.
1609
+ * @returns The merged filter, without a `preset` value.
1610
+ *
1611
+ * @example
1612
+ * ```ts
1613
+ * mergeFilters([{ date, preset: 'today' }, { minPrice: 100 }]);
1614
+ * // { date, minPrice: 100 }
1615
+ * ```
1616
+ */ function mergeFilters(filters) {
1617
+ var merged = mergeObjects(filters);
1618
+ delete merged.preset;
1619
+ return merged;
1620
+ }
1621
+ /**
1622
+ * Combines the latest value of each filter observable into a single merged filter observable.
1623
+ *
1624
+ * Emits once every input has emitted, then again whenever any input emits a new value.
1625
+ * Consecutive emissions with equal values are suppressed.
1626
+ *
1627
+ * @param filterObs - Filter observables to combine. Every observable must emit at least once for the result to emit.
1628
+ * @param mergeFn - Function used to merge the latest filters. Defaults to {@link mergeFilters}.
1629
+ * @returns Observable of the merged filter.
1630
+ *
1631
+ * @example
1632
+ * ```ts
1633
+ * const filter$ = combineFilters([dateFilter$, attributesFilter$]);
1634
+ * ```
1635
+ */ function combineFilters(filterObs) {
1636
+ var mergeFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : mergeFilters;
1637
+ return combineLatest(filterObs).pipe(map(function(filters) {
1638
+ return mergeFn(filters);
1639
+ }), distinctUntilObjectValuesChanged(), shareReplay(1));
1640
+ }
1641
+
1642
+ function _array_like_to_array$6(arr, len) {
1449
1643
  if (len == null || len > arr.length) len = arr.length;
1450
1644
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
1451
1645
  return arr2;
1452
1646
  }
1453
1647
  function _array_without_holes$3(arr) {
1454
- if (Array.isArray(arr)) return _array_like_to_array$7(arr);
1648
+ if (Array.isArray(arr)) return _array_like_to_array$6(arr);
1455
1649
  }
1456
1650
  function _class_call_check$6(instance, Constructor) {
1457
1651
  if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
@@ -1489,15 +1683,15 @@ function _non_iterable_spread$3() {
1489
1683
  throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
1490
1684
  }
1491
1685
  function _to_consumable_array$3(arr) {
1492
- return _array_without_holes$3(arr) || _iterable_to_array$3(arr) || _unsupported_iterable_to_array$7(arr) || _non_iterable_spread$3();
1686
+ return _array_without_holes$3(arr) || _iterable_to_array$3(arr) || _unsupported_iterable_to_array$6(arr) || _non_iterable_spread$3();
1493
1687
  }
1494
- function _unsupported_iterable_to_array$7(o, minLen) {
1688
+ function _unsupported_iterable_to_array$6(o, minLen) {
1495
1689
  if (!o) return;
1496
- if (typeof o === "string") return _array_like_to_array$7(o, minLen);
1690
+ if (typeof o === "string") return _array_like_to_array$6(o, minLen);
1497
1691
  var n = Object.prototype.toString.call(o).slice(8, -1);
1498
1692
  if (n === "Object" && o.constructor) n = o.constructor.name;
1499
1693
  if (n === "Map" || n === "Set") return Array.from(n);
1500
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$7(o, minLen);
1694
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$6(o, minLen);
1501
1695
  }
1502
1696
  /**
1503
1697
  * Manages a collection of reactive filters keyed by string identifiers.
@@ -1541,6 +1735,56 @@ function _unsupported_iterable_to_array$7(o, minLen) {
1541
1735
  },
1542
1736
  {
1543
1737
  /**
1738
+ * Returns an observable of the filters for all the given keys merged into a single filter.
1739
+ *
1740
+ * Lets several filter controls each own a separate key, so one control saving its filter never overwrites the fields set by another.
1741
+ * Keys later in the array win when two keys set the same field. The `preset` field is removed from the merged result.
1742
+ *
1743
+ * Every key must have a value (for example, a default added with {@link addDefaultFilterObs}) before the merged filter emits.
1744
+ *
1745
+ * @param keys - Filter map keys to merge, in priority order.
1746
+ * @param mergeFn - Function used to merge the filters. Defaults to {@link mergeFilters}.
1747
+ * @returns Observable that emits the merged filter whenever any of the keys' filters change.
1748
+ *
1749
+ * @example
1750
+ * ```ts
1751
+ * filterMap.addDefaultFilterObs('date', of({}));
1752
+ * filterMap.addDefaultFilterObs('attributes', of({}));
1753
+ *
1754
+ * filterMap.mergedFilterForKeys(['date', 'attributes']).subscribe((filter) => console.log(filter));
1755
+ * ```
1756
+ */ key: "mergedFilterForKeys",
1757
+ value: function mergedFilterForKeys(keys, mergeFn) {
1758
+ var _this = this;
1759
+ var filterObs = keys.map(function(key) {
1760
+ return _this.filterForKey(key);
1761
+ });
1762
+ return combineFilters(filterObs, mergeFn);
1763
+ }
1764
+ },
1765
+ {
1766
+ /**
1767
+ * Sets the filter for the given key.
1768
+ *
1769
+ * The filter becomes the key's value the same way an emission from one of the key's filter observables would,
1770
+ * and stays the key's value until one of those observables emits a new filter.
1771
+ *
1772
+ * Use to update a key from outside its filter controls, e.g. a "Clear" button that resets the key.
1773
+ *
1774
+ * @param key - Filter map key.
1775
+ * @param filter - Filter to set.
1776
+ *
1777
+ * @example
1778
+ * ```ts
1779
+ * filterMap.setFilterForKey('users', { active: true });
1780
+ * ```
1781
+ */ key: "setFilterForKey",
1782
+ value: function setFilterForKey(key, filter) {
1783
+ this._itemForKey(key).setFilter(filter);
1784
+ }
1785
+ },
1786
+ {
1787
+ /**
1544
1788
  * Sets a default filter observable for the given key, used as a fallback when no explicit filter is set.
1545
1789
  *
1546
1790
  * @param key - Filter map key.
@@ -1670,18 +1914,28 @@ function _unsupported_iterable_to_array$7(o, minLen) {
1670
1914
  }();
1671
1915
  var FilterMapItem = /*#__PURE__*/ function() {
1672
1916
  function FilterMapItem(dbxFilterMap, key) {
1917
+ var _this = this;
1673
1918
  _class_call_check$6(this, FilterMapItem);
1674
1919
  _define_property$b(this, "_dbxFilterMap", void 0);
1675
1920
  _define_property$b(this, "_key", void 0);
1676
1921
  _define_property$b(this, "_i", 0);
1677
1922
  _define_property$b(this, "_source", new FilterSourceInstance());
1678
1923
  _define_property$b(this, "_obs", new BehaviorSubject([]));
1924
+ _define_property$b(this, "_setFilter", new BehaviorSubject(undefined));
1679
1925
  _define_property$b(this, "_obs$", this._obs.pipe(switchMap(function(x) {
1680
1926
  return merge.apply(void 0, _to_consumable_array$3(x.map(function(y) {
1681
1927
  return y.obs;
1682
1928
  })));
1683
1929
  }), distinctUntilChanged()));
1684
- _define_property$b(this, "filter$", this._source.initialFilter$);
1930
+ /**
1931
+ * The source's filter, or the filter from setFilter() until the source emits a new filter after it was set.
1932
+ *
1933
+ * The set filter is held rather than added as another filter observable, so it is not lost if nothing is subscribed yet,
1934
+ * and does not replay over newer values when the key's filter observables change.
1935
+ */ _define_property$b(this, "filter$", this._setFilter.pipe(// the source's current value is older than the set filter, so only values the source emits afterwards replace it
1936
+ switchMap(function(setFilter) {
1937
+ return setFilter == null ? _this._source.initialFilter$ : concat(of(setFilter), _this._source.initialFilter$.pipe(skipReplayedValues()));
1938
+ }), distinctUntilChanged(), shareReplay(1)));
1685
1939
  this._dbxFilterMap = dbxFilterMap;
1686
1940
  this._key = key;
1687
1941
  }
@@ -1704,6 +1958,12 @@ var FilterMapItem = /*#__PURE__*/ function() {
1704
1958
  this._source.setDefaultFilter(obs);
1705
1959
  }
1706
1960
  },
1961
+ {
1962
+ key: "setFilter",
1963
+ value: function setFilter(filter) {
1964
+ this._setFilter.next(filter);
1965
+ }
1966
+ },
1707
1967
  {
1708
1968
  key: "addFilterObs",
1709
1969
  value: function addFilterObs(obs) {
@@ -1754,6 +2014,7 @@ var FilterMapItem = /*#__PURE__*/ function() {
1754
2014
  });
1755
2015
  this._source.destroy();
1756
2016
  this._obs.complete();
2017
+ this._setFilter.complete();
1757
2018
  }
1758
2019
  }
1759
2020
  ]);
@@ -2101,13 +2362,13 @@ function _ts_generator$1(thisArg, body) {
2101
2362
  });
2102
2363
  }
2103
2364
 
2104
- function _array_like_to_array$6(arr, len) {
2365
+ function _array_like_to_array$5(arr, len) {
2105
2366
  if (len == null || len > arr.length) len = arr.length;
2106
2367
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
2107
2368
  return arr2;
2108
2369
  }
2109
2370
  function _array_without_holes$2(arr) {
2110
- if (Array.isArray(arr)) return _array_like_to_array$6(arr);
2371
+ if (Array.isArray(arr)) return _array_like_to_array$5(arr);
2111
2372
  }
2112
2373
  function _iterable_to_array$2(iter) {
2113
2374
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) {
@@ -2118,15 +2379,15 @@ function _non_iterable_spread$2() {
2118
2379
  throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
2119
2380
  }
2120
2381
  function _to_consumable_array$2(arr) {
2121
- return _array_without_holes$2(arr) || _iterable_to_array$2(arr) || _unsupported_iterable_to_array$6(arr) || _non_iterable_spread$2();
2382
+ return _array_without_holes$2(arr) || _iterable_to_array$2(arr) || _unsupported_iterable_to_array$5(arr) || _non_iterable_spread$2();
2122
2383
  }
2123
- function _unsupported_iterable_to_array$6(o, minLen) {
2384
+ function _unsupported_iterable_to_array$5(o, minLen) {
2124
2385
  if (!o) return;
2125
- if (typeof o === "string") return _array_like_to_array$6(o, minLen);
2386
+ if (typeof o === "string") return _array_like_to_array$5(o, minLen);
2126
2387
  var n = Object.prototype.toString.call(o).slice(8, -1);
2127
2388
  if (n === "Object" && o.constructor) n = o.constructor.name;
2128
2389
  if (n === "Map" || n === "Set") return Array.from(n);
2129
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$6(o, minLen);
2390
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$5(o, minLen);
2130
2391
  }
2131
2392
  function distinctUntilArrayLengthChanges(inputGetArray) {
2132
2393
  var getArray = inputGetArray !== null && inputGetArray !== void 0 ? inputGetArray : function(value) {
@@ -2806,122 +3067,6 @@ function _object_spread_props$8(target, source) {
2806
3067
  }));
2807
3068
  }
2808
3069
 
2809
- function _array_like_to_array$5(arr, len) {
2810
- if (len == null || len > arr.length) len = arr.length;
2811
- for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
2812
- return arr2;
2813
- }
2814
- function _array_with_holes$3(arr) {
2815
- if (Array.isArray(arr)) return arr;
2816
- }
2817
- function _iterable_to_array_limit$3(arr, i) {
2818
- var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
2819
- if (_i == null) return;
2820
- var _arr = [];
2821
- var _n = true;
2822
- var _d = false;
2823
- var _s, _e;
2824
- try {
2825
- for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
2826
- _arr.push(_s.value);
2827
- if (i && _arr.length === i) break;
2828
- }
2829
- } catch (err) {
2830
- _d = true;
2831
- _e = err;
2832
- } finally{
2833
- try {
2834
- if (!_n && _i["return"] != null) _i["return"]();
2835
- } finally{
2836
- if (_d) throw _e;
2837
- }
2838
- }
2839
- return _arr;
2840
- }
2841
- function _non_iterable_rest$3() {
2842
- throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
2843
- }
2844
- function _sliced_to_array$3(arr, i) {
2845
- return _array_with_holes$3(arr) || _iterable_to_array_limit$3(arr, i) || _unsupported_iterable_to_array$5(arr, i) || _non_iterable_rest$3();
2846
- }
2847
- function _unsupported_iterable_to_array$5(o, minLen) {
2848
- if (!o) return;
2849
- if (typeof o === "string") return _array_like_to_array$5(o, minLen);
2850
- var n = Object.prototype.toString.call(o).slice(8, -1);
2851
- if (n === "Object" && o.constructor) n = o.constructor.name;
2852
- if (n === "Map" || n === "Set") return Array.from(n);
2853
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$5(o, minLen);
2854
- }
2855
- /**
2856
- * Combines `startWith` and {@link tapFirst} to initialize an observable pipe with a side-effect on the first emission.
2857
- *
2858
- * Emits the `initial` value first, then taps on the first emitted value to run the provided callback.
2859
- *
2860
- * @param tap - Side-effect function called with the first value.
2861
- * @param initial - Optional starting value emitted before the source.
2862
- * @param skipFirst - If true, skips tapping the initial value.
2863
- * @returns An operator that initializes the pipe with a tap.
2864
- */ function initialize(tap, initial, skipFirst) {
2865
- return function(source) {
2866
- var subscriber = source.pipe(startWith(initial), tapFirst(function(x) {
2867
- return tap(x);
2868
- }, initial == null || skipFirst));
2869
- return subscriber;
2870
- };
2871
- }
2872
- /**
2873
- * Executes a side-effect on the first emission from the observable, then passes all values through.
2874
- *
2875
- * @param tap - The side-effect function to call once.
2876
- * @param skipFirst - If true, skips the very first emission before tapping.
2877
- * @returns An operator that taps the first value.
2878
- */ function tapFirst(tap) {
2879
- var skipFirst = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false;
2880
- return skipWhile(function(value) {
2881
- var i = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 0;
2882
- tap(value);
2883
- return i === 0 && !skipFirst;
2884
- });
2885
- }
2886
- /**
2887
- * Wraps an observable so that it never emits `complete` until it is unsubscribed.
2888
- *
2889
- * The subscription will never have `complete()` called since it only triggers after unsubscription.
2890
- * Use `finalize()` for additional cleanup.
2891
- *
2892
- * @param obs - The source observable to wrap.
2893
- * @returns An observable that only completes on unsubscription.
2894
- */ function preventComplete(obs) {
2895
- var complete = new BehaviorSubject(0);
2896
- return combineLatest([
2897
- obs,
2898
- complete
2899
- ]).pipe(map(function(param) {
2900
- var _param = _sliced_to_array$3(param, 1), x = _param[0];
2901
- return x;
2902
- }), shareReplay(1), finalize(function() {
2903
- complete.complete();
2904
- }));
2905
- }
2906
- /**
2907
- * Creates a cold observable that defers execution of the getter until subscription, then shares the result.
2908
- *
2909
- * Unlike `from()`, the promise/observable is not created until the first subscriber connects.
2910
- *
2911
- * @param getter - Factory that returns a Promise or Observable.
2912
- * @returns A shared observable that defers execution until subscription.
2913
- *
2914
- * @example
2915
- * ```ts
2916
- * const data$ = lazyFrom(() => fetch('/api/data').then(r => r.json()));
2917
- * // The fetch is not called until data$ is subscribed to
2918
- * ```
2919
- */ function lazyFrom(getter) {
2920
- return of(undefined).pipe(mergeMap(function() {
2921
- return from(getter());
2922
- }), shareReplay(1));
2923
- }
2924
-
2925
3070
  /**
2926
3071
  * RxJS operator that filters an array to unique items based on a key reader function.
2927
3072
  *
@@ -6982,4 +7127,4 @@ function _define_property(obj, key, value) {
6982
7127
  };
6983
7128
  }
6984
7129
 
6985
- export { AssetLoader, DEFAULT_ASYNC_PUSHER_THROTTLE, DEFAULT_FACTORY_TIMER_INTERVAL, DEFAULT_ITEM_PAGE_ITERATOR_MAX, DEFAULT_LOADING_EVENT_FOR_LOADING_PAIR_FUNCTION, DEFAULT_LOCK_SET_TIME_LOCK_KEY, FilterMap, FilterMapKeyInstance, FilterSource, FilterSourceConnector, FilterSourceInstance, ItemPageIterationInstance, ItemPageIterator, LoadingStateType, LockSet, MultiSubscriptionObject, PresetFilterSource, SimpleLoadingContext, SubscriptionObject, ValuesLoadingContext, WorkInstance, accumulatorCurrentPageListLoadingState, accumulatorFlattenPageListLoadingState, areAllLoadingStatesFinishedLoading, arrayValueFromFinishedLoadingState, asObservable, asObservableFromGetter, assetFolder, assetLoaderAssetInstance, assetLoaderFromGetFn, asyncPusher, asyncPusherCache, beginLoading, beginLoadingPage, catchLoadingStateErrorWithOperator, checkIs, cleanup, cleanupDestroyable, combineLatestFromArrayObsFn, combineLatestFromMapValuesObsFn, combineLatestFromObject, combineLatestMapFrom, combineLoadingStates, combineLoadingStatesStatus, currentValueFromLoadingState, delegatedAssetLoader, distinctLoadingState, distinctUntilArrayLengthChanges, distinctUntilHasDifferentValues, distinctUntilItemsHaveDifferentValues, distinctUntilItemsValueChanges, distinctUntilKeysChange, distinctUntilMapHasDifferentKeys, distinctUntilModelIdChange, distinctUntilModelKeyChange, distinctUntilObjectKeyChange, distinctUntilObjectValuesChanged, emitAfterDelay, emitDelayObs, errorFromLoadingState, errorOnEmissionsInPeriod, errorPageResult, errorResult, factoryTimer, fetchAssetLoader, filterIfObjectValuesUnchanged, filterItemsWithObservableDecision, filterMaybe, filterMaybeArray, filterMaybeStrict, filterUnique, filterWithSearchString, flattenAccumulatorResultItemArray, idleLoadingState, incrementingNumberTimer, initialize, invertObservableDecision, isAnyLoadingStateInLoadingState, isItemPageIteratorResultEndResult, isListLoadingStateWithEmptyValue, isLoading, isLoadingStateEqual, isLoadingStateFinishedLoading, isLoadingStateFinishedLoadingWithDefinedValue, isLoadingStateFinishedLoadingWithError, isLoadingStateInErrorState, isLoadingStateInIdleState, isLoadingStateInLoadingState, isLoadingStateInSuccessState, isLoadingStateLoading, isLoadingStateMetadataEqual, isLoadingStateWithDefinedValue, isLoadingStateWithError, isLoadingStateWithStateType, isNot, isPageLoadingState, isPageLoadingStateMetadataEqual, itemAccumulator, itemAccumulatorNextPageUntilResultsCount, iterationHasNextAndCanLoadMore, iteratorNextPageUntilMaxPageLoadLimit, iteratorNextPageUntilPage, keyValueMap, lazyFrom, listLoadingStateContext, loadingStateContext, loadingStateFromObs, loadingStateHasNextPage, loadingStateType, loadingStateValue, loadingStateWithValueType, localAsset, makeCheckIsFunction, makeIsModifiedFunction, makeIsModifiedFunctionObservable, makeMapFilterWithPresetFn, makeReturnIfIsFunction, mapEachAsync, mapFilterWithPreset, mapForEach, mapIf, mapIsListLoadingStateWithEmptyValue, mapItemIteration, mapKeysIntersectionToArray, mapLoadingState, mapLoadingStateResults, mapLoadingStateValueFunction, mapLoadingStateValueWithOperator, mapMaybe, mappedPageItemIteration, maybeValueFromObservableOrValue, maybeValueFromObservableOrValueGetter, memoryAssetLoader, mergeLoadingStateWithError, mergeLoadingStateWithLoading, mergeLoadingStateWithValue, mergeLoadingStates, mergeLoadingStatesArray, multiKeyValueMap, onFalseToTrue, onLockSetNextUnlock, onMatchDelta, onTrueToFalse, pageItemAccumulatorCurrentPage, pageLoadingStateFromObs, pipeIf, preventComplete, promiseFromLoadingState, randomDelay, randomDelayWithRandomFunction, remoteAsset, remoteAssetBaseUrl, returnIfIs, scanBuildArray, scanCount, scanIntoArray, setContainsAllValuesFrom, setContainsAnyValueFrom, setContainsNoValueFrom, skipAfterExpiration, skipAllInitialMaybe, skipExpired, skipInitialMaybe, skipMaybes, skipUntilExpiration, skipUntilTimeElapsedAfterLastEmission, startWithBeginLoading, successPageResult, successResult, switchMapFilterMaybe, switchMapMaybe, switchMapMaybeDefault, switchMapMaybeLoadingContextStream, switchMapObject, switchMapOnBoolean, switchMapToDefault, switchMapWhileFalse, switchMapWhileTrue, takeAfterTimeElapsedSinceLastEmission, tapAfterTimeout, tapFirst, tapLog, tapOnLoadingStateSuccess, tapOnLoadingStateType, throwErrorAfterTimeout, throwErrorFromLoadingStateError, timeoutStartWith, toExpiration, useAsObservable, useFirst, valueFromFinishedLoadingState, valueFromLoadingState, valueFromObservableOrValue, valueFromObservableOrValueGetter, workFactory, workFactoryForConfigFactory };
7130
+ export { AssetLoader, DEFAULT_ASYNC_PUSHER_THROTTLE, DEFAULT_FACTORY_TIMER_INTERVAL, DEFAULT_ITEM_PAGE_ITERATOR_MAX, DEFAULT_LOADING_EVENT_FOR_LOADING_PAIR_FUNCTION, DEFAULT_LOCK_SET_TIME_LOCK_KEY, FilterMap, FilterMapKeyInstance, FilterSource, FilterSourceConnector, FilterSourceInstance, ItemPageIterationInstance, ItemPageIterator, LoadingStateType, LockSet, MultiSubscriptionObject, PresetFilterSource, SimpleLoadingContext, SubscriptionObject, ValuesLoadingContext, WorkInstance, accumulatorCurrentPageListLoadingState, accumulatorFlattenPageListLoadingState, areAllLoadingStatesFinishedLoading, arrayValueFromFinishedLoadingState, asObservable, asObservableFromGetter, assetFolder, assetLoaderAssetInstance, assetLoaderFromGetFn, asyncPusher, asyncPusherCache, beginLoading, beginLoadingPage, catchLoadingStateErrorWithOperator, checkIs, cleanup, cleanupDestroyable, combineFilters, combineLatestFromArrayObsFn, combineLatestFromMapValuesObsFn, combineLatestFromObject, combineLatestMapFrom, combineLoadingStates, combineLoadingStatesStatus, currentValueFromLoadingState, delegatedAssetLoader, distinctLoadingState, distinctUntilArrayLengthChanges, distinctUntilHasDifferentValues, distinctUntilItemsHaveDifferentValues, distinctUntilItemsValueChanges, distinctUntilKeysChange, distinctUntilMapHasDifferentKeys, distinctUntilModelIdChange, distinctUntilModelKeyChange, distinctUntilObjectKeyChange, distinctUntilObjectValuesChanged, emitAfterDelay, emitDelayObs, errorFromLoadingState, errorOnEmissionsInPeriod, errorPageResult, errorResult, factoryTimer, fetchAssetLoader, filterIfObjectValuesUnchanged, filterItemsWithObservableDecision, filterMaybe, filterMaybeArray, filterMaybeStrict, filterUnique, filterWithSearchString, flattenAccumulatorResultItemArray, idleLoadingState, incrementingNumberTimer, initialize, invertObservableDecision, isAnyLoadingStateInLoadingState, isItemPageIteratorResultEndResult, isListLoadingStateWithEmptyValue, isLoading, isLoadingStateEqual, isLoadingStateFinishedLoading, isLoadingStateFinishedLoadingWithDefinedValue, isLoadingStateFinishedLoadingWithError, isLoadingStateInErrorState, isLoadingStateInIdleState, isLoadingStateInLoadingState, isLoadingStateInSuccessState, isLoadingStateLoading, isLoadingStateMetadataEqual, isLoadingStateWithDefinedValue, isLoadingStateWithError, isLoadingStateWithStateType, isNot, isPageLoadingState, isPageLoadingStateMetadataEqual, itemAccumulator, itemAccumulatorNextPageUntilResultsCount, iterationHasNextAndCanLoadMore, iteratorNextPageUntilMaxPageLoadLimit, iteratorNextPageUntilPage, keyValueMap, lazyFrom, listLoadingStateContext, loadingStateContext, loadingStateFromObs, loadingStateHasNextPage, loadingStateType, loadingStateValue, loadingStateWithValueType, localAsset, makeCheckIsFunction, makeIsModifiedFunction, makeIsModifiedFunctionObservable, makeMapFilterWithPresetFn, makeReturnIfIsFunction, mapEachAsync, mapFilterWithPreset, mapForEach, mapIf, mapIsListLoadingStateWithEmptyValue, mapItemIteration, mapKeysIntersectionToArray, mapLoadingState, mapLoadingStateResults, mapLoadingStateValueFunction, mapLoadingStateValueWithOperator, mapMaybe, mappedPageItemIteration, maybeValueFromObservableOrValue, maybeValueFromObservableOrValueGetter, memoryAssetLoader, mergeFilters, mergeLoadingStateWithError, mergeLoadingStateWithLoading, mergeLoadingStateWithValue, mergeLoadingStates, mergeLoadingStatesArray, multiKeyValueMap, onFalseToTrue, onLockSetNextUnlock, onMatchDelta, onTrueToFalse, pageItemAccumulatorCurrentPage, pageLoadingStateFromObs, pipeIf, preventComplete, promiseFromLoadingState, randomDelay, randomDelayWithRandomFunction, remoteAsset, remoteAssetBaseUrl, returnIfIs, scanBuildArray, scanCount, scanIntoArray, setContainsAllValuesFrom, setContainsAnyValueFrom, setContainsNoValueFrom, skipAfterExpiration, skipAllInitialMaybe, skipExpired, skipInitialMaybe, skipMaybes, skipReplayedValues, skipUntilExpiration, skipUntilTimeElapsedAfterLastEmission, startWithBeginLoading, successPageResult, successResult, switchMapFilterMaybe, switchMapMaybe, switchMapMaybeDefault, switchMapMaybeLoadingContextStream, switchMapObject, switchMapOnBoolean, switchMapToDefault, switchMapWhileFalse, switchMapWhileTrue, takeAfterTimeElapsedSinceLastEmission, tapAfterTimeout, tapFirst, tapLog, tapOnLoadingStateSuccess, tapOnLoadingStateType, throwErrorAfterTimeout, throwErrorFromLoadingStateError, timeoutStartWith, toExpiration, useAsObservable, useFirst, valueFromFinishedLoadingState, valueFromLoadingState, valueFromObservableOrValue, valueFromObservableOrValueGetter, workFactory, workFactoryForConfigFactory };
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@dereekb/rxjs",
3
- "version": "14.8.0",
3
+ "version": "14.10.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
7
7
  "rxjs": "^7.8.2",
8
- "@dereekb/util": "14.8.0"
8
+ "@dereekb/util": "14.10.0"
9
9
  },
10
10
  "exports": {
11
11
  "./package.json": "./package.json",
@@ -1,3 +1,4 @@
1
+ import { type Maybe } from '@dereekb/util';
1
2
  import { type Observable } from 'rxjs';
2
3
  /**
3
4
  * Abstract base class for providing reactive filter state.
@@ -68,3 +69,29 @@ export declare abstract class FilterSourceConnector<F = unknown> {
68
69
  */
69
70
  abstract connectWithSource(filterSource: FilterSource<F>): void;
70
71
  }
72
+ /**
73
+ * Converts a filter to and from a JSON-safe value, e.g. to save it to storage.
74
+ *
75
+ * Declare one alongside a filter type that has fields JSON cannot represent, such as Date or Set fields.
76
+ *
77
+ * @typeParam F - The filter type.
78
+ * @typeParam J - The JSON-safe value the filter is converted to.
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * const MY_FILTER_JSON_CONVERTER: FilterJsonConverter<MyFilter, MyFilterJson> = {
83
+ * toJson: (filter) => ({ ...filter, date: filter.date?.toISOString() }),
84
+ * fromJson: (json) => ({ ...json, date: json.date ? new Date(json.date) : undefined })
85
+ * };
86
+ * ```
87
+ */
88
+ export interface FilterJsonConverter<F, J = unknown> {
89
+ /**
90
+ * Converts the filter to a JSON-safe value.
91
+ */
92
+ readonly toJson: (filter: F) => J;
93
+ /**
94
+ * Converts a JSON value back to the filter. Returns null/undefined if the value is not a valid filter.
95
+ */
96
+ readonly fromJson: (json: J) => Maybe<F>;
97
+ }
@@ -1,6 +1,7 @@
1
1
  import { type ObservableOrValue } from '../rxjs/getter';
2
2
  import { type Observable } from 'rxjs';
3
3
  import { type FilterSource, type FilterSourceConnector } from './filter';
4
+ import { type MergeFiltersFunction } from './filter.merge';
4
5
  import { type Destroyable, type Maybe } from '@dereekb/util';
5
6
  /**
6
7
  * String key used to identify a specific filter entry within a {@link FilterMap}.
@@ -35,6 +36,44 @@ export declare class FilterMap<F> implements Destroyable {
35
36
  * @returns Observable that emits the current filter value for the key.
36
37
  */
37
38
  filterForKey(key: FilterMapKey): Observable<F>;
39
+ /**
40
+ * Returns an observable of the filters for all the given keys merged into a single filter.
41
+ *
42
+ * Lets several filter controls each own a separate key, so one control saving its filter never overwrites the fields set by another.
43
+ * Keys later in the array win when two keys set the same field. The `preset` field is removed from the merged result.
44
+ *
45
+ * Every key must have a value (for example, a default added with {@link addDefaultFilterObs}) before the merged filter emits.
46
+ *
47
+ * @param keys - Filter map keys to merge, in priority order.
48
+ * @param mergeFn - Function used to merge the filters. Defaults to {@link mergeFilters}.
49
+ * @returns Observable that emits the merged filter whenever any of the keys' filters change.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * filterMap.addDefaultFilterObs('date', of({}));
54
+ * filterMap.addDefaultFilterObs('attributes', of({}));
55
+ *
56
+ * filterMap.mergedFilterForKeys(['date', 'attributes']).subscribe((filter) => console.log(filter));
57
+ * ```
58
+ */
59
+ mergedFilterForKeys(keys: FilterMapKey[], mergeFn?: MergeFiltersFunction<F>): Observable<F>;
60
+ /**
61
+ * Sets the filter for the given key.
62
+ *
63
+ * The filter becomes the key's value the same way an emission from one of the key's filter observables would,
64
+ * and stays the key's value until one of those observables emits a new filter.
65
+ *
66
+ * Use to update a key from outside its filter controls, e.g. a "Clear" button that resets the key.
67
+ *
68
+ * @param key - Filter map key.
69
+ * @param filter - Filter to set.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * filterMap.setFilterForKey('users', { active: true });
74
+ * ```
75
+ */
76
+ setFilterForKey(key: FilterMapKey, filter: F): void;
38
77
  /**
39
78
  * Sets a default filter observable for the given key, used as a fallback when no explicit filter is set.
40
79
  *
@@ -0,0 +1,43 @@
1
+ import { type Maybe } from '@dereekb/util';
2
+ import { type Observable } from 'rxjs';
3
+ /**
4
+ * Function that merges several partial filters into a single filter.
5
+ *
6
+ * Filters are applied left-to-right, so the last filter in the array has the highest priority.
7
+ */
8
+ export type MergeFiltersFunction<F> = (filters: Maybe<Partial<F>>[]) => F;
9
+ /**
10
+ * Merges several partial filters into a single filter.
11
+ *
12
+ * Filters are applied left-to-right, so the last filter wins when two filters set the same field.
13
+ * `undefined` values never overwrite a previously set value.
14
+ *
15
+ * The `preset` field is removed from the result, since a merged filter cannot represent more than one preset.
16
+ * Each source filter keeps its own preset, so any preset menu bound to a single source still shows its selection.
17
+ *
18
+ * @param filters - Partial filters (or null/undefined) to merge.
19
+ * @returns The merged filter, without a `preset` value.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * mergeFilters([{ date, preset: 'today' }, { minPrice: 100 }]);
24
+ * // { date, minPrice: 100 }
25
+ * ```
26
+ */
27
+ export declare function mergeFilters<F extends object>(filters: Maybe<Partial<F>>[]): F;
28
+ /**
29
+ * Combines the latest value of each filter observable into a single merged filter observable.
30
+ *
31
+ * Emits once every input has emitted, then again whenever any input emits a new value.
32
+ * Consecutive emissions with equal values are suppressed.
33
+ *
34
+ * @param filterObs - Filter observables to combine. Every observable must emit at least once for the result to emit.
35
+ * @param mergeFn - Function used to merge the latest filters. Defaults to {@link mergeFilters}.
36
+ * @returns Observable of the merged filter.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * const filter$ = combineFilters([dateFilter$, attributesFilter$]);
41
+ * ```
42
+ */
43
+ export declare function combineFilters<F extends object>(filterObs: Observable<Maybe<Partial<F>>>[], mergeFn?: MergeFiltersFunction<F>): Observable<F>;
@@ -1,4 +1,5 @@
1
1
  export * from './filter.map';
2
+ export * from './filter.merge';
2
3
  export * from './filter.preset';
3
4
  export * from './filter.source';
4
5
  export * from './filter';
@@ -1,5 +1,5 @@
1
1
  import { type Getter, type Maybe } from '@dereekb/util';
2
- import { type Observable, type MonoTypeOperatorFunction } from 'rxjs';
2
+ import { Observable, type MonoTypeOperatorFunction } from 'rxjs';
3
3
  /**
4
4
  * Combines `startWith` and {@link tapFirst} to initialize an observable pipe with a side-effect on the first emission.
5
5
  *
@@ -19,6 +19,21 @@ export declare function initialize<T>(tap: (value: Maybe<T>) => void, initial?:
19
19
  * @returns An operator that taps the first value.
20
20
  */
21
21
  export declare function tapFirst<T>(tap: (value: T) => void, skipFirst?: boolean): MonoTypeOperatorFunction<T>;
22
+ /**
23
+ * RxJS operator that ignores the values the source emits synchronously while it is being subscribed to, such as the
24
+ * current value of a BehaviorSubject or the latest value replayed by `shareReplay()`. Only values emitted afterwards pass through.
25
+ *
26
+ * @returns Operator that skips the values replayed on subscription.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const value = new BehaviorSubject(1);
31
+ * value.pipe(skipReplayedValues()).subscribe(console.log);
32
+ * value.next(2);
33
+ * // Output: 2
34
+ * ```
35
+ */
36
+ export declare function skipReplayedValues<T>(): MonoTypeOperatorFunction<T>;
22
37
  /**
23
38
  * Wraps an observable so that it never emits `complete` until it is unsubscribed.
24
39
  *