@dereekb/dbx-form 9.25.4 → 9.25.6

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.
@@ -7,7 +7,7 @@ import { Injectable, SkipSelf, Directive, Injector, Optional, Component, Inject,
7
7
  import { FieldType } from '@ngx-formly/material';
8
8
  import { switchMap, first, tap, map, distinctUntilChanged, shareReplay, of, combineLatestWith, BehaviorSubject, filter, combineLatest, EMPTY, startWith, throttleTime } from 'rxjs';
9
9
  import { filterMaybe, distinctUntilHasDifferentValues, SubscriptionObject, asObservableFromGetter, asObservable } from '@dereekb/rxjs';
10
- import { DateCellScheduleDayCode, expandDateCellScheduleDayCodesToDayOfWeekSet, dateCellTimingStartsAtForStartOfDay, dateCellTimingRelativeIndexFactory, dateCellDayOfWeekFactory, findMaxDate, findMinDate, isSameDateRange, isSameDateDay, isSameDate, dateCellTimingDateFactory, expandDateCellScheduleRange, formatToISO8601DayString, changeDateCellScheduleDateRangeToTimezone, isSameDateCellScheduleDateRange, dateCellTimingRelativeIndexArrayFactory, isInfiniteDateRange, copyDateCellScheduleDateFilterConfig, dateTimezoneUtcNormal, dateCellScheduleDateFilter, fullDateCellScheduleRange, dateCellTimingTimezoneNormalInstance, expandDateCellScheduleDayCodes, fullWeekDateCellScheduleDayCodes, dateCellScheduleDayCodesAreSetsEquivalent, simplifyDateCellScheduleDayCodes, isDateInDateRangeFunction, isDateWithinDateCellRangeFunction, dateCellScheduleEncodedWeek, dateCellTimingStartDateFactory, enabledDaysFromDateCellScheduleDayCodes, dateCellScheduleDayCodesFromEnabledDays, formatToMonthDayString, dateRange, DateRangeType } from '@dereekb/date';
10
+ import { DateCellScheduleDayCode, expandDateCellScheduleDayCodesToDayOfWeekSet, dateCellTimingStartsAtForStartOfDay, dateCellTimingRelativeIndexFactory, dateCellDayOfWeekFactory, dateTimezoneUtcNormal, findMaxDate, findMinDate, isSameDateRange, isSameDateDay, isSameDate, dateCellTimingDateFactory, expandDateCellScheduleRange, formatToISO8601DayString, changeDateCellScheduleDateRangeToTimezone, isSameDateCellScheduleDateRange, dateCellTimingRelativeIndexArrayFactory, isInfiniteDateRange, copyDateCellScheduleDateFilterConfig, SYSTEM_DATE_TIMEZONE_UTC_NORMAL_INSTANCE, dateCellScheduleDateFilter, fullDateCellScheduleRange, dateCellTimingTimezoneNormalInstance, expandDateCellScheduleDayCodes, fullWeekDateCellScheduleDayCodes, dateCellScheduleDayCodesAreSetsEquivalent, simplifyDateCellScheduleDayCodes, isDateInDateRangeFunction, isDateWithinDateCellRangeFunction, dateCellScheduleEncodedWeek, dateCellTimingStartDateFactory, enabledDaysFromDateCellScheduleDayCodes, dateCellScheduleDayCodesFromEnabledDays, formatToMonthDayString, dateRange, DateRangeType } from '@dereekb/date';
11
11
  import { isInAllowedDaysOfWeekSet, mapValuesToSet, unique, mergeArrays, iterableToArray, range, toggleInSet, removeFromSet, addToSet, isIndexNumberInIndexRangeFunction, minAndMaxNumber, getDaysOfWeekNames, reduceBooleansWithAnd, mergeObjects, KeyValueTypleValueFilter } from '@dereekb/util';
12
12
  import { ComponentStore } from '@ngrx/component-store';
13
13
  import { startOfDay, endOfDay, isBefore, endOfWeek } from 'date-fns';
@@ -116,11 +116,28 @@ function initialCalendarScheduleSelectionState() {
116
116
  cellContentFactory: defaultCalendarScheduleSelectionCellContentFactory
117
117
  };
118
118
  }
119
- function calendarScheduleMinDate(x) {
120
- return findMaxDate([x.filter?.start, x.minMaxDateRange?.start]);
121
- }
122
- function calendarScheduleMaxDate(x) {
123
- return findMinDate([x.filter?.end, x.minMaxDateRange?.end]);
119
+ /**
120
+ * This is used in cases where the dates in the min/max date range need to be shifted to the filter's timezone.
121
+ *
122
+ * This is because the index factory is always in system timezone, but when selecting all/none with a filter we need to use the filter's timezone.
123
+ *
124
+ * @param x
125
+ * @returns
126
+ */
127
+ function calendarScheduleMinAndMaxDateRangeRelativeToFilter(x) {
128
+ let input = x;
129
+ if (x.filter?.timezone && x.minMaxDateRange != null) {
130
+ const filterNormal = dateTimezoneUtcNormal(x.filter.timezone);
131
+ const transformFn = filterNormal.transformFunction('systemDateToTargetDate');
132
+ input = {
133
+ filter: x.filter,
134
+ minMaxDateRange: {
135
+ start: x.minMaxDateRange.start ? transformFn(x.minMaxDateRange.start) : undefined,
136
+ end: x.minMaxDateRange.end ? transformFn(x.minMaxDateRange.end) : undefined
137
+ }
138
+ };
139
+ }
140
+ return calendarScheduleMinAndMaxDateRange(input);
124
141
  }
125
142
  function calendarScheduleMinAndMaxDateRange(x) {
126
143
  return {
@@ -128,6 +145,12 @@ function calendarScheduleMinAndMaxDateRange(x) {
128
145
  end: calendarScheduleMaxDate(x) || undefined
129
146
  };
130
147
  }
148
+ function calendarScheduleMinDate(x) {
149
+ return findMaxDate([x.filter?.start, x.minMaxDateRange?.start]);
150
+ }
151
+ function calendarScheduleMaxDate(x) {
152
+ return findMinDate([x.filter?.end, x.minMaxDateRange?.end]);
153
+ }
131
154
  function calendarScheduleStartBeingUsedFromFilter(x) {
132
155
  return x.computeSelectionResultRelativeToFilter && x.filter?.start != null; // may be using either
133
156
  }
@@ -332,21 +355,34 @@ function updateStateWithFilter(state, inputFilter) {
332
355
  let filterStart = null; // the start date that will be used/set on the filter.
333
356
  if (inputFilter) {
334
357
  filter = copyDateCellScheduleDateFilterConfig(inputFilter); // copy filter
358
+ let nextFilterTimezone; // only set if inputFilter.start or inputFilter.startsAt
335
359
  // configure filter start
336
360
  if (inputFilter.start) {
337
361
  filterStart = inputFilter.start;
362
+ // if no timezone is specified, then use the system timezone and align filterStart to the start of the day.
363
+ if (!inputFilter.timezone) {
364
+ filterStart = SYSTEM_DATE_TIMEZONE_UTC_NORMAL_INSTANCE.startOfDayInTargetTimezone(inputFilter.startsAt);
365
+ nextFilterTimezone = systemTimezone;
366
+ }
367
+ else {
368
+ nextFilterTimezone = inputFilter.timezone;
369
+ }
338
370
  }
339
371
  else if (inputFilter.startsAt) {
340
372
  if (inputFilter.timezone) {
373
+ // if no timezone is provided, use startsAt as-is
341
374
  const timezoneNormal = dateTimezoneUtcNormal(inputFilter.timezone);
342
375
  filterStart = timezoneNormal.startOfDayInTargetTimezone(inputFilter.startsAt);
376
+ nextFilterTimezone = inputFilter.timezone;
343
377
  }
344
378
  else {
345
- filterStart = inputFilter.startsAt;
346
- filter.timezone = systemTimezone;
379
+ // set to the start of today in the system timezone.
380
+ filterStart = SYSTEM_DATE_TIMEZONE_UTC_NORMAL_INSTANCE.startOfDayInTargetTimezone(inputFilter.startsAt);
381
+ nextFilterTimezone = systemTimezone;
347
382
  }
348
383
  }
349
384
  filter.start = filterStart ?? undefined;
385
+ filter.timezone = nextFilterTimezone;
350
386
  // configure exclusions
351
387
  if (exclusions?.length) {
352
388
  enabledFilter = {
@@ -372,17 +408,46 @@ function updateStateWithFilter(state, inputFilter) {
372
408
  * If the input filter has a start date, use that as the relative start to ensure indexes are compared the same,
373
409
  * otherwise use the state's start. This is important for the index calculations.
374
410
  */
375
- enabledFilter.start = filter?.start ?? state.start;
411
+ let finalEnabledStart;
412
+ let finalEnabledTimezone;
413
+ // filter?.start ?? state.start;
414
+ if (!enabledFilter.start) {
415
+ // use the current state's start, but make sure it is in the proper timezone.
416
+ if (enabledFilter.timezone) {
417
+ const timezoneNormal = dateTimezoneUtcNormal(enabledFilter.timezone);
418
+ finalEnabledStart = timezoneNormal.startOfDayInTargetTimezone(state.start); // get the start of the day for the current start
419
+ finalEnabledTimezone = enabledFilter.timezone;
420
+ }
421
+ else {
422
+ finalEnabledStart = state.start;
423
+ finalEnabledTimezone = systemTimezone;
424
+ }
425
+ }
426
+ else if (!enabledFilter.timezone) {
427
+ finalEnabledTimezone = systemTimezone;
428
+ const timezoneNormal = dateTimezoneUtcNormal(finalEnabledTimezone);
429
+ finalEnabledStart = timezoneNormal.startOfDayInTargetTimezone(enabledFilter.start); // get the start of the day for the target timezone
430
+ }
431
+ else {
432
+ finalEnabledStart = enabledFilter.start;
433
+ finalEnabledTimezone = enabledFilter.timezone;
434
+ }
435
+ enabledFilter.start = finalEnabledStart;
436
+ enabledFilter.timezone = finalEnabledTimezone;
376
437
  // create the filter
377
438
  isEnabledFilterDay = dateCellScheduleDateFilter(enabledFilter);
378
439
  }
379
440
  state = { ...state, filter, isEnabledFilterDay };
380
441
  // For the same reason as above, use the filter's start date as the relative start if applicable.
381
442
  if (filter && filter.start) {
382
- const start = filter.start;
383
- state.start = start;
384
- state.indexFactory = dateCellTimingRelativeIndexFactory({ startsAt: start, timezone: filter.timezone ?? systemTimezone });
385
- state.indexDayOfWeek = dateCellDayOfWeekFactory(start);
443
+ let startForSystemTimezone = filter.start;
444
+ if (filter.timezone) {
445
+ const timezoneNormal = dateTimezoneUtcNormal(filter.timezone);
446
+ startForSystemTimezone = timezoneNormal.systemDateToTargetDate(filter.start); // get the start of the day for the system timezone
447
+ }
448
+ state.start = startForSystemTimezone;
449
+ state.indexFactory = dateCellTimingRelativeIndexFactory({ startsAt: startForSystemTimezone, timezone: systemTimezone });
450
+ state.indexDayOfWeek = dateCellDayOfWeekFactory(startForSystemTimezone);
386
451
  }
387
452
  // attempt to re-apply the initial selection state once filter is applied
388
453
  if (state.initialSelectionState) {
@@ -505,7 +570,7 @@ function finalizeUpdateStateWithChangedScheduleDays(previousState, nextState) {
505
570
  }
506
571
  function updateStateWithChangedDates(state, change) {
507
572
  const { allowedDaysOfWeek, indexFactory, indexDayOfWeek, inputStart: currentInputStart, inputEnd: currentInputEnd, minMaxDateRange, filter } = state;
508
- const { start: minDate, end: maxDate } = calendarScheduleMinAndMaxDateRange(state);
573
+ const { start: minDate, end: maxDate } = calendarScheduleMinAndMaxDateRangeRelativeToFilter(state);
509
574
  let inputStart = currentInputStart;
510
575
  let inputEnd = currentInputEnd;
511
576
  /**
@@ -590,8 +655,8 @@ function noSelectionCalendarScheduleSelectionState(state) {
590
655
  return finalizeNewCalendarScheduleSelectionState({ ...state, toggledIndexes: new Set(), inputStart: null, inputEnd: null });
591
656
  }
592
657
  function updateStateWithChangedRange(state, change) {
593
- const { inputStart: currentInputStart, inputEnd: currentInputEnd, indexFactory, minMaxDateRange } = state;
594
- const { start: minDate, end: maxDate } = minMaxDateRange ?? {};
658
+ const { inputStart: currentInputStart, inputEnd: currentInputEnd, indexFactory } = state;
659
+ const { start: minDate, end: maxDate } = calendarScheduleMinAndMaxDateRange(state);
595
660
  const inputStart = startOfDay(change.inputStart);
596
661
  const inputEnd = startOfDay(change.inputEnd); // midnight of the last day
597
662
  const isValidRange = minDate != null || maxDate != null ? isDateInDateRangeFunction({ start: minDate ?? undefined, end: maxDate ?? undefined }) : () => true;
@@ -633,7 +698,7 @@ function isEnabledDayInCalendarScheduleSelectionState(state) {
633
698
  };
634
699
  }
635
700
  function computeScheduleSelectionValue(state) {
636
- const { indexFactory, allowedDaysOfWeek, effectiveScheduleDays, indexDayOfWeek, computeSelectionResultRelativeToFilter, filter, systemTimezone } = state;
701
+ const { indexFactory: systemIndexFactory, allowedDaysOfWeek, effectiveScheduleDays, indexDayOfWeek, computeSelectionResultRelativeToFilter, filter, systemTimezone } = state;
637
702
  let timezone = systemTimezone;
638
703
  const rangeAndExclusion = computeScheduleSelectionRangeAndExclusion(state);
639
704
  if (rangeAndExclusion == null) {
@@ -647,12 +712,16 @@ function computeScheduleSelectionValue(state) {
647
712
  // If computeSelectionResultRelativeToFilter is true, then we need to offset the values to be relative to that start.
648
713
  if (computeSelectionResultRelativeToFilter && filter?.start) {
649
714
  start = filter.start; // always start at the filter's start date
715
+ let startInSystemTimezone = start;
650
716
  if (filter.timezone) {
651
717
  timezone = filter.timezone;
652
718
  const filterNormal = dateTimezoneUtcNormal(timezone);
653
719
  end = filterNormal.startOfDayInTargetTimezone(end);
720
+ startInSystemTimezone = filterNormal.systemDateToTargetDate(start); // convert the start to the system timezone normal for deriving the index
654
721
  }
655
- const filterStartIndexOffset = indexFactory(rangeStart) - indexFactory(start);
722
+ const rangeStartIndex = systemIndexFactory(rangeStart);
723
+ const startIndex = systemIndexFactory(startInSystemTimezone);
724
+ const filterStartIndexOffset = rangeStartIndex - startIndex;
656
725
  filterOffsetExcludedRange = range(0, filterStartIndexOffset);
657
726
  indexOffset = indexOffset - filterStartIndexOffset;
658
727
  }