@dereekb/dbx-form 13.11.1 → 13.11.2

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.
@@ -6,7 +6,7 @@ import { isPast, addSeconds, startOfDay, addMinutes, addDays, isBefore } from 'd
6
6
  import { map, of, shareReplay, switchMap, first, exhaustMap, catchError, delay, filter, combineLatest, distinctUntilChanged, BehaviorSubject, Subject, startWith, throttleTime, scan, timer, merge, EMPTY, skip, debounceTime, combineLatestWith, interval, tap, withLatestFrom, mergeMap } from 'rxjs';
7
7
  import * as i1$2 from '@dereekb/dbx-core';
8
8
  import { DbxActionContextStoreSourceInstance, cleanLockSet, cleanSubscription, completeOnDestroy, cleanWithLockSet, GetValuePipe, DateDistancePipe, TimeDistancePipe, DbxInjectionComponent, mergeDbxInjectionComponentConfigs } from '@dereekb/dbx-core';
9
- import { makeIsModifiedFunctionObservable, asObservable, LockSet, filterMaybe, switchMapFilterMaybe, scanCount, errorOnEmissionsInPeriod, asObservableFromGetter, tapLog, maybeValueFromObservableOrValue, valueFromFinishedLoadingState, switchMapMaybeDefault, skipAllInitialMaybe, successResult, startWithBeginLoading, skipUntilTimeElapsedAfterLastEmission, mapLoadingStateResults, isLoadingStateWithDefinedValue, isLoadingStateLoading, beginLoading, mapLoadingStateValueWithOperator, loadingStateContext, distinctUntilHasDifferentValues, SimpleLoadingContext, listLoadingStateContext, mapIsListLoadingStateWithEmptyValue, isLoadingStateInLoadingState, asyncPusherCache } from '@dereekb/rxjs';
9
+ import { makeIsModifiedFunctionObservable, asObservable, LockSet, filterMaybe, switchMapFilterMaybe, scanCount, errorOnEmissionsInPeriod, asObservableFromGetter, maybeValueFromObservableOrValue, valueFromFinishedLoadingState, switchMapMaybeDefault, skipAllInitialMaybe, successResult, startWithBeginLoading, skipUntilTimeElapsedAfterLastEmission, mapLoadingStateResults, isLoadingStateWithDefinedValue, isLoadingStateLoading, beginLoading, mapLoadingStateValueWithOperator, loadingStateContext, distinctUntilHasDifferentValues, SimpleLoadingContext, listLoadingStateContext, mapIsListLoadingStateWithEmptyValue, isLoadingStateInLoadingState, asyncPusherCache } from '@dereekb/rxjs';
10
10
  import { toObservable, toSignal, rxResource } from '@angular/core/rxjs-interop';
11
11
  import { BooleanStringKeyArrayUtility, iterablesAreSetEquivalent, filterUndefinedValues, filterMaybeArrayValues, filterUniqueValues, areEqualPOJOValuesUsingPojoFilter, NOOP_MODIFIER, asArray, mergeArrays, filterNullAndUndefinedValues, objectHasNoKeys, mapMaybeFunction, isWebsiteUrlWithPrefix, websiteUrlDetails, transformStringFunction, US_STATE_CODE_STRING_REGEX, ZIP_CODE_STRING_REGEX, LAT_LNG_PATTERN, transformNumberFunction, DOLLAR_AMOUNT_PRECISION, stripObject, getValueFromGetter, asGetter, dateFromMinuteOfDay, dateToMinuteOfDay, isISO8601DayStringStart, mapIdentityFunction, isDate, MS_IN_MINUTE, isMonthDaySlashDate, filterFromPOJO, TIME_UNIT_SHORT_LABEL_MAP, timeUnitToMilliseconds, ALL_TIME_UNITS, minutesToHoursAndMinutes, millisecondsToTimeUnit, hoursAndMinutesToTimeUnit, isE164PhoneNumber as isE164PhoneNumber$1, isValidPhoneExtensionNumber, e164PhoneNumberExtensionPair, e164PhoneNumberFromE164PhoneNumberExtensionPair, mergeArraysIntoArray, convertMaybeToArray, lastValue, filterEmptyArrayValues, setContainsAllValues, addToSetCopy, setsAreEquivalent, makeValuesGroupMap, sortByStringFunction, separateValues, isSelectedDecisionFunctionFactory, readKeysFrom, hasDifferentValues, capitalizeFirstLetter, objectIsEmpty, mergeObjectsFunction, filterFromPOJOFunction, mergeObjects, addPlusPrefixToNumber, searchStringFilterFunction, caseInsensitiveFilterByIndexOfDecisionFactory, arrayToMap, firstValue, cachedGetter, makeGetter, asDecisionFunction, TIME_UNIT_LABEL_MAP, HAS_WEBSITE_DOMAIN_NAME_REGEX, KeyValueTypleValueFilter, valuesFromPOJO, allObjectsAreEqual, isNumberDivisibleBy, nearestDivisibleValues, concatArrays } from '@dereekb/util';
12
12
  import * as i1 from '@angular/forms';
@@ -1166,6 +1166,11 @@ function isEmptyFormValue(val) {
1166
1166
  * from a form value object. Also removes keys whose values become empty objects
1167
1167
  * `{}` after recursive stripping.
1168
1168
  *
1169
+ * Arrays are recursed into so that empties inside nested objects are stripped,
1170
+ * but array length and item indices are preserved — primitive empty values
1171
+ * (e.g. `NaN`, `''`) inside an array stay in place, since shifting indices would
1172
+ * change the semantics of chip/list-style array fields.
1173
+ *
1169
1174
  * This normalizes ng-forge output to match ngx-formly behavior, where the model
1170
1175
  * only includes keys that have been explicitly set by the user.
1171
1176
  *
@@ -1176,6 +1181,9 @@ function isEmptyFormValue(val) {
1176
1181
  *
1177
1182
  * stripEmptyForgeValues({ section: { a: "", b: "" } })
1178
1183
  * // → {}
1184
+ *
1185
+ * stripEmptyForgeValues({ items: [{ amount: NaN, name: 'a' }, { amount: 5 }] })
1186
+ * // → { items: [{ name: 'a' }, { amount: 5 }] }
1179
1187
  * ```
1180
1188
  *
1181
1189
  * @param value - The form value object to clean
@@ -1183,18 +1191,24 @@ function isEmptyFormValue(val) {
1183
1191
  */
1184
1192
  function stripEmptyForgeValues(value) {
1185
1193
  let result;
1186
- if (value == null || typeof value !== 'object' || Array.isArray(value)) {
1194
+ if (value == null || typeof value !== 'object' || value instanceof Date) {
1187
1195
  result = value;
1188
1196
  }
1197
+ else if (Array.isArray(value)) {
1198
+ result = value.map((item) => stripEmptyForgeValues(item));
1199
+ }
1189
1200
  else {
1190
1201
  const stripped = {};
1191
1202
  for (const [key, val] of Object.entries(value)) {
1192
1203
  if (isEmptyFormValue(val)) {
1193
1204
  continue;
1194
1205
  }
1195
- if (typeof val === 'object' && !Array.isArray(val) && !(val instanceof Date)) {
1206
+ if (typeof val === 'object' && !(val instanceof Date)) {
1196
1207
  const cleaned = stripEmptyForgeValues(val);
1197
- if (cleaned != null && Object.keys(cleaned).length > 0) {
1208
+ if (Array.isArray(cleaned)) {
1209
+ stripped[key] = cleaned;
1210
+ }
1211
+ else if (cleaned != null && Object.keys(cleaned).length > 0) {
1198
1212
  stripped[key] = cleaned;
1199
1213
  }
1200
1214
  }
@@ -1236,7 +1250,7 @@ class DbxForgeFormContext {
1236
1250
  */
1237
1251
  stripInternalKeys = true;
1238
1252
  /**
1239
- * When true (default), keys whose values are empty (`null`, `undefined`, or `""`)
1253
+ * When true (default), keys whose values are empty (`null`, `undefined`, `""`, or `NaN`)
1240
1254
  * are stripped from the form value before emission. This normalizes ng-forge output
1241
1255
  * to match ngx-formly behavior, where the model only includes keys that have been
1242
1256
  * explicitly set by the user.
@@ -1323,7 +1337,7 @@ class DbxForgeFormContext {
1323
1337
  _isValid = new BehaviorSubject(false);
1324
1338
  _setValue = new BehaviorSubject(undefined);
1325
1339
  _reset = new BehaviorSubject(new Date());
1326
- _internalConfig$ = this._config.pipe(tapLog('internal config'), scan((acc, config) => {
1340
+ _internalConfig$ = this._config.pipe(scan((acc, config) => {
1327
1341
  let result;
1328
1342
  if (config) {
1329
1343
  if (acc?.input !== config) {
@@ -1337,8 +1351,8 @@ class DbxForgeFormContext {
1337
1351
  result = undefined;
1338
1352
  }
1339
1353
  return result;
1340
- }, undefined), tapLog('internal config result'), shareReplay(1));
1341
- config$ = this._internalConfig$.pipe(filterMaybe(), map(({ config }) => config), tapLog('config'), shareReplay(1));
1354
+ }, undefined), shareReplay(1));
1355
+ config$ = this._internalConfig$.pipe(filterMaybe(), map(({ config }) => config), shareReplay(1));
1342
1356
  /**
1343
1357
  * Form event stream that restarts on each reset, mirroring the formly form's
1344
1358
  * switchMap-on-reset pattern. This ensures that each resetForm() produces a fresh
@@ -1577,7 +1591,7 @@ class DbxForgeFormComponent {
1577
1591
  _disabledSub = cleanSubscription();
1578
1592
  dynamicForm = viewChild(DynamicForm, ...(ngDevMode ? [{ debugName: "dynamicForm" }] : /* istanbul ignore next */ []));
1579
1593
  formValue = signal({}, { ...(ngDevMode ? { debugName: "formValue" } : /* istanbul ignore next */ {}), equal: (a, b) => _forgeFormValueEqual(a, b, this._context) });
1580
- configSignal = toSignal(this._context.config$.pipe(tapLog('forge.config')), { initialValue: undefined });
1594
+ configSignal = toSignal(this._context.config$, { initialValue: undefined });
1581
1595
  _changesCount = signal(0, ...(ngDevMode ? [{ debugName: "_changesCount" }] : /* istanbul ignore next */ []));
1582
1596
  _lastResetAt = signal(new Date(), ...(ngDevMode ? [{ debugName: "_lastResetAt" }] : /* istanbul ignore next */ []));
1583
1597
  _isReset = signal(true, ...(ngDevMode ? [{ debugName: "_isReset" }] : /* istanbul ignore next */ []));
@@ -1996,8 +2010,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
1996
2010
  class DbxFormLoggerDirective {
1997
2011
  form = inject(DbxForm, { host: true });
1998
2012
  constructor() {
1999
- cleanSubscription(this.form.stream$.subscribe((event) => {
2000
- console.log('dbxFormLogger - stream: ', event);
2013
+ cleanSubscription(combineLatest([this.form.getValue(), this.form.stream$]).subscribe(([currentValue, event]) => {
2014
+ console.log('dbxFormLogger - stream: ', { currentValue, event });
2001
2015
  }));
2002
2016
  }
2003
2017
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxFormLoggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
@@ -8105,8 +8119,8 @@ class DbxForgeValueSelectionFieldComponent {
8105
8119
  addClearOptionSignal = computed(() => this.props()?.addClearOption, ...(ngDevMode ? [{ debugName: "addClearOptionSignal" }] : /* istanbul ignore next */ []));
8106
8120
  addClearOption$ = toObservable(this.addClearOptionSignal).pipe(distinctUntilChanged());
8107
8121
  inputOptionsSignal = computed(() => this.props()?.options, ...(ngDevMode ? [{ debugName: "inputOptionsSignal" }] : /* istanbul ignore next */ []));
8108
- inputOptions$ = toObservable(this.inputOptionsSignal).pipe(tapLog('yyy'), maybeValueFromObservableOrValue());
8109
- resolvedOptions$ = combineLatest([this.inputOptions$, this.addClearOption$]).pipe(tapLog('xxxx'), map(([options, addClearOption]) => resolveForgeSelectionOptions(options ?? [], addClearOption ?? false)));
8122
+ inputOptions$ = toObservable(this.inputOptionsSignal).pipe(maybeValueFromObservableOrValue());
8123
+ resolvedOptions$ = combineLatest([this.inputOptions$, this.addClearOption$]).pipe(map(([options, addClearOption]) => resolveForgeSelectionOptions(options ?? [], addClearOption ?? false)));
8110
8124
  resolvedOptionsSignal = toSignal(this.resolvedOptions$);
8111
8125
  multipleSignal = computed(() => this.props()?.multiple ?? false, ...(ngDevMode ? [{ debugName: "multipleSignal" }] : /* istanbul ignore next */ []));
8112
8126
  effectiveAppearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "effectiveAppearance" }] : /* istanbul ignore next */ []));