@gooddata/sdk-ui-filters 11.50.0-alpha.2 → 11.50.0-alpha.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"useElementsFilterController.d.ts","sourceRoot":"","sources":["../../../src/AttributeFilter/hooks/useElementsFilterController.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,6BAA6B,EAAE,MAAM,2BAA2B,CAAC;AACxG,OAAO,EACH,KAAK,qCAAqC,EAC1C,KAAK,mBAAmB,EAExB,KAAK,iBAAiB,EAEtB,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,MAAM,EAed,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,gBAAgB,EAAsB,MAAM,kBAAkB,CAAC;AAG7E,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAYlF,OAAO,EAEH,KAAK,wBAAwB,EAEhC,MAAM,YAAY,CAAC;AA06BpB;;;GAGG;AACH,MAAM,WAAW,8BAA8B;IAC3C,OAAO,EAAE,kBAAkB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,gBAAgB,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wBAAwB,CAAC,EAAE,6BAA6B,EAAE,GAAG,IAAI,CAAC;IAClE,mBAAmB,CAAC,EAAE,CAAC,mBAAmB,GAAG,mBAAmB,CAAC,EAAE,GAAG,IAAI,CAAC;IAC3E,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;IACnC,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC5C,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,iBAAiB,CAAC,EAAE,wBAAwB,KAAK,IAAI,CAAC;IAChG,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACrC,eAAe,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,aAAa,EAAE,qCAAqC,CAAC;IACrD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qDAAqD,CAAC,EAAE,OAAO,CAAC;IAChE,wCAAwC,EAAE,OAAO,CAAC;IAClD,mCAAmC,EAAE,OAAO,CAAC;IAC7C,+BAA+B,EAAE,OAAO,CAAC;IACzC,oCAAoC,EAAE,OAAO,CAAC;IAC9C,4BAA4B,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACjE,oGAAoG;IACpG,oBAAoB,EAAE,UAAU,GAAG,MAAM,CAAC;IAC1C,+GAA+G;IAC/G,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,8BAA8B,GAAG,wBAAwB,CA4M3G"}
1
+ {"version":3,"file":"useElementsFilterController.d.ts","sourceRoot":"","sources":["../../../src/AttributeFilter/hooks/useElementsFilterController.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,6BAA6B,EAAE,MAAM,2BAA2B,CAAC;AACxG,OAAO,EACH,KAAK,qCAAqC,EAC1C,KAAK,mBAAmB,EAExB,KAAK,iBAAiB,EAEtB,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,MAAM,EAed,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,gBAAgB,EAAsB,MAAM,kBAAkB,CAAC;AAG7E,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAYlF,OAAO,EAEH,KAAK,wBAAwB,EAEhC,MAAM,YAAY,CAAC;AAw7BpB;;;GAGG;AACH,MAAM,WAAW,8BAA8B;IAC3C,OAAO,EAAE,kBAAkB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,gBAAgB,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wBAAwB,CAAC,EAAE,6BAA6B,EAAE,GAAG,IAAI,CAAC;IAClE,mBAAmB,CAAC,EAAE,CAAC,mBAAmB,GAAG,mBAAmB,CAAC,EAAE,GAAG,IAAI,CAAC;IAC3E,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;IACnC,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC5C,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,iBAAiB,CAAC,EAAE,wBAAwB,KAAK,IAAI,CAAC;IAChG,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACrC,eAAe,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,aAAa,EAAE,qCAAqC,CAAC;IACrD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qDAAqD,CAAC,EAAE,OAAO,CAAC;IAChE,wCAAwC,EAAE,OAAO,CAAC;IAClD,mCAAmC,EAAE,OAAO,CAAC;IAC7C,+BAA+B,EAAE,OAAO,CAAC;IACzC,oCAAoC,EAAE,OAAO,CAAC;IAC9C,4BAA4B,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACjE,oGAAoG;IACpG,oBAAoB,EAAE,UAAU,GAAG,MAAM,CAAC;IAC1C,+GAA+G;IAC/G,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,8BAA8B,GAAG,wBAAwB,CA4M3G"}
@@ -285,8 +285,17 @@ function useInitOrReload(handler, props, supportsKeepingDependentFiltersSelectio
285
285
  !isEqual(limitingAttributeFilters, handler.getLimitingAttributeFilters());
286
286
  const limitingDateFiltersChanged = shouldIncludeLimitingFilters && !isEqual(limitingDateFilters, handler.getLimitingDateFilters());
287
287
  const getFilterChanged = (filter, handler) => {
288
+ // When the handler migrated the filter internally to a different label (displayAs
289
+ // handling of filters saved directly with a secondary label), the handler's filter
290
+ // never equals the label-based prop again. A prop that is still identical to the
291
+ // filter the handler was initialized from is then not an external change — without
292
+ // this, a working-selection edit racing the parent's display-form sync re-render is
293
+ // mistaken for an external update and resets the in-progress selection.
294
+ const isMigratedToOtherLabel = !areObjRefsEqual(filterObjRef(filter), filterObjRef(handler.getFilter()));
295
+ const isUnchangedOriginalOfMigrated = isMigratedToOtherLabel && areFiltersEqual(filter, handler.getOriginalFilter());
288
296
  return (!areFiltersEqual(filter, handler.getFilter()) &&
289
- !areFiltersEqual(filter, handler.getFilterToDisplay()));
297
+ !areFiltersEqual(filter, handler.getFilterToDisplay()) &&
298
+ !isUnchangedOriginalOfMigrated);
290
299
  };
291
300
  // When text→elements mode switch happens, resetForModeSwitch already committed the target
292
301
  // selection (restored List selection or "All") to the handler. Suppress filterChanged so we
@@ -0,0 +1,15 @@
1
+ import { type IDateFilterConfig } from "@gooddata/sdk-model";
2
+ import { type IDateFilterOptionsByType } from "../interfaces/index.js";
3
+ /**
4
+ * Converts a workspace date filter config — as stored on the backend — into the date filter options
5
+ * the {@link DateFilter} component consumes (its `filterOptions` prop).
6
+ *
7
+ * @remarks
8
+ * Extracted from sdk-ui-dashboard so non-dashboard hosts (e.g. conditional formatting's date
9
+ * condition picker) consume the same workspace preset catalog; dashboards re-export it.
10
+ *
11
+ * @param config - date filter config from backend
12
+ * @alpha
13
+ */
14
+ export declare function convertDateFilterConfigToDateFilterOptions(config: IDateFilterConfig): IDateFilterOptionsByType;
15
+ //# sourceMappingURL=DateFilterConfigConversions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DateFilterConfigConversions.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/utils/DateFilterConfigConversions.ts"],"names":[],"mappings":"AAKA,OAAO,EAIH,KAAK,iBAAiB,EAIzB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAGH,KAAK,wBAAwB,EAIhC,MAAM,wBAAwB,CAAC;AAOhC;;;;;;;;;;GAUG;AACH,wBAAgB,0CAA0C,CACtD,MAAM,EAAE,iBAAiB,GAC1B,wBAAwB,CAgB1B"}
@@ -0,0 +1,86 @@
1
+ // (C) 2019-2026 GoodData Corporation
2
+ import { endOfDay, format, startOfDay, subMonths } from "date-fns";
3
+ import { groupBy, max, min } from "lodash-es";
4
+ import { removeEmptyKeysFromDateFilterOptions } from "./OptionUtils.js";
5
+ // date-fns tokens of the platform date-time format (Platform.ts holds the moment spelling).
6
+ const PLATFORM_DATE_FORMAT = "yyyy-MM-dd HH:mm";
7
+ /**
8
+ * Converts a workspace date filter config — as stored on the backend — into the date filter options
9
+ * the {@link DateFilter} component consumes (its `filterOptions` prop).
10
+ *
11
+ * @remarks
12
+ * Extracted from sdk-ui-dashboard so non-dashboard hosts (e.g. conditional formatting's date
13
+ * condition picker) consume the same workspace preset catalog; dashboards re-export it.
14
+ *
15
+ * @param config - date filter config from backend
16
+ * @alpha
17
+ */
18
+ export function convertDateFilterConfigToDateFilterOptions(config) {
19
+ const allTime = convertAllTime(config.allTime);
20
+ const emptyValues = convertEmptyValues(config.emptyValues);
21
+ const absoluteForm = convertAbsoluteForm(config.absoluteForm);
22
+ const relativeForm = convertRelativeForm(config.relativeForm);
23
+ const absolutePreset = convertAbsolutePresets(config.absolutePresets);
24
+ const relativePreset = convertRelativePresets(config.relativePresets);
25
+ return removeEmptyKeysFromDateFilterOptions({
26
+ allTime,
27
+ emptyValues,
28
+ absoluteForm,
29
+ absolutePreset,
30
+ relativeForm,
31
+ relativePreset,
32
+ });
33
+ }
34
+ function convertAllTime(filter) {
35
+ return (filter && {
36
+ ...filter,
37
+ type: "allTime",
38
+ });
39
+ }
40
+ function convertEmptyValues(filter) {
41
+ return (filter && {
42
+ ...filter,
43
+ type: "emptyValues",
44
+ });
45
+ }
46
+ function convertAbsoluteForm(filter) {
47
+ const now = new Date();
48
+ return (filter && {
49
+ ...filter,
50
+ from: format(startOfDay(subMonths(now, 1)), PLATFORM_DATE_FORMAT),
51
+ to: format(endOfDay(now), PLATFORM_DATE_FORMAT),
52
+ type: "absoluteForm",
53
+ });
54
+ }
55
+ function convertRelativeForm(filter) {
56
+ return (filter && {
57
+ from: undefined,
58
+ // we order the granularities anyway, this lets the user to config the default
59
+ granularity: filter.availableGranularities[0],
60
+ localIdentifier: filter.localIdentifier,
61
+ name: filter.name,
62
+ to: undefined,
63
+ type: "relativeForm",
64
+ visible: filter.visible,
65
+ });
66
+ }
67
+ function convertAbsolutePresets(filters) {
68
+ return filters?.map((preset) => sanitizeDateFilterOption({
69
+ ...preset,
70
+ type: "absolutePreset",
71
+ }));
72
+ }
73
+ function convertRelativePresets(filters) {
74
+ return (filters &&
75
+ groupBy(filters.map((preset) => sanitizeDateFilterOption({
76
+ ...preset,
77
+ type: "relativePreset",
78
+ })), (preset) => preset.granularity));
79
+ }
80
+ function sanitizeDateFilterOption(option) {
81
+ return {
82
+ ...option,
83
+ from: min([option.from, option.to]),
84
+ to: max([option.from, option.to]),
85
+ };
86
+ }
@@ -0,0 +1,46 @@
1
+ import { type IDashboardDateFilter } from "@gooddata/sdk-model";
2
+ import { type DateFilterOption, type IDateFilterOptionsByType } from "../interfaces/index.js";
3
+ /**
4
+ * A matched date filter option together with the reconstructed exclude-current-period state.
5
+ *
6
+ * @alpha
7
+ */
8
+ export interface IDateFilterOptionInfo {
9
+ dateFilterOption: DateFilterOption;
10
+ excludeCurrentPeriod: boolean;
11
+ }
12
+ /**
13
+ * Tries to match a preset or a form with the provided value. Prioritizes the provided option if possible.
14
+ *
15
+ * @remarks
16
+ * This is to handle cases when user picked a form and filled values that match an existing preset.
17
+ * In those cases we want to show the form as picked even though a preset would otherwise be preferred.
18
+ *
19
+ * @param dateFilter - value to match against
20
+ * @param availableOptions - date options available
21
+ * @param preferredOptionId - id of the option that should be matched first if possible
22
+ * @alpha
23
+ */
24
+ export declare function matchDateFilterToDateFilterOptionWithPreference(dateFilter: IDashboardDateFilter | undefined, availableOptions: IDateFilterOptionsByType, preferredOptionId: string | undefined): IDateFilterOptionInfo;
25
+ /**
26
+ * Tries to match a preset or a form with the provided value. Prioritizes presets over forms.
27
+ * @param dateFilterValue - value to match against
28
+ * @param availableOptions - date options available
29
+ * @alpha
30
+ */
31
+ export declare function matchDateFilterToDateFilterOption(dateFilter: IDashboardDateFilter | undefined, availableOptions: IDateFilterOptionsByType): IDateFilterOptionInfo;
32
+ /**
33
+ * Flattens the provided date filter options. The flattening maintains a stable, predefined order in which
34
+ * the options should be rendered by the date filter component.
35
+ *
36
+ * @param dateFilterOptions - available options to flatten
37
+ * @alpha
38
+ */
39
+ export declare function flattenDateFilterOptions(dateFilterOptions: IDateFilterOptionsByType): DateFilterOption[];
40
+ /**
41
+ * Finds the date filter option whose stored value equals the provided filter's value.
42
+ *
43
+ * @alpha
44
+ */
45
+ export declare function findDateFilterOptionByValue(dateFilter: IDashboardDateFilter, dateFilterOptions: IDateFilterOptionsByType): DateFilterOption | undefined;
46
+ //# sourceMappingURL=DateFilterOptionMapping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DateFilterOptionMapping.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/utils/DateFilterOptionMapping.ts"],"names":[],"mappings":"AAIA,OAAO,EAIH,KAAK,oBAAoB,EAK5B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAKhC,MAAM,wBAAwB,CAAC;AAYhC;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IAClC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,oBAAoB,EAAE,OAAO,CAAC;CACjC;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,+CAA+C,CAC3D,UAAU,EAAE,oBAAoB,GAAG,SAAS,EAC5C,gBAAgB,EAAE,wBAAwB,EAC1C,iBAAiB,EAAE,MAAM,GAAG,SAAS,GACtC,qBAAqB,CAmBvB;AAED;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAC7C,UAAU,EAAE,oBAAoB,GAAG,SAAS,EAC5C,gBAAgB,EAAE,wBAAwB,GAC3C,qBAAqB,CA6EvB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,iBAAiB,EAAE,wBAAwB,GAAG,gBAAgB,EAAE,CAcxG;AAsDD;;;;GAIG;AACH,wBAAgB,2BAA2B,CACvC,UAAU,EAAE,oBAAoB,EAChC,iBAAiB,EAAE,wBAAwB,gCAG9C"}
@@ -0,0 +1,279 @@
1
+ // (C) 2021-2026 GoodData Corporation
2
+ import { compact } from "lodash-es";
3
+ import { isAllTimeDashboardDateFilter, isLowerBound, isUpperBound, } from "@gooddata/sdk-model";
4
+ import { isAbsoluteDateFilterOption, isRelativeDateFilterOption, } from "../interfaces/index.js";
5
+ import { revertExcludedCurrentPeriodRange } from "./PeriodExclusion.js";
6
+ const VIRTUAL_PRESET_IDENTIFIER = "GDC__VIRTUAL_PRESET";
7
+ const virtualPresetBase = {
8
+ localIdentifier: VIRTUAL_PRESET_IDENTIFIER,
9
+ name: "",
10
+ visible: false,
11
+ };
12
+ const isAllTimeDateFilter = (dateFilter) => dateFilter && isAllTimeDashboardDateFilter(dateFilter);
13
+ function applyEmptyValueHandling(dateFilterOption, dateFilter) {
14
+ const emptyValueHandling = dateFilter?.dateFilter.emptyValueHandling;
15
+ return emptyValueHandling ? { ...dateFilterOption, emptyValueHandling } : dateFilterOption;
16
+ }
17
+ /**
18
+ * Tries to match a preset or a form with the provided value. Prioritizes the provided option if possible.
19
+ *
20
+ * @remarks
21
+ * This is to handle cases when user picked a form and filled values that match an existing preset.
22
+ * In those cases we want to show the form as picked even though a preset would otherwise be preferred.
23
+ *
24
+ * @param dateFilter - value to match against
25
+ * @param availableOptions - date options available
26
+ * @param preferredOptionId - id of the option that should be matched first if possible
27
+ * @alpha
28
+ */
29
+ export function matchDateFilterToDateFilterOptionWithPreference(dateFilter, availableOptions, preferredOptionId) {
30
+ const preferredOption = preferredOptionId
31
+ ? findDateFilterOptionById(preferredOptionId, availableOptions)
32
+ : undefined;
33
+ const isAllTime = isAllTimeDateFilter(dateFilter);
34
+ // we only really need to handle the cases when the selected option is a form
35
+ // other cases are correctly handled by the unbiased matching function
36
+ if (dateFilter &&
37
+ !isAllTime &&
38
+ (preferredOption?.type === "absoluteForm" || preferredOption?.type === "relativeForm") &&
39
+ canReconstructFormForStoredFilter(availableOptions, dateFilter)) {
40
+ return reconstructFormForStoredFilter(availableOptions, dateFilter);
41
+ }
42
+ return matchDateFilterToDateFilterOption(dateFilter, availableOptions);
43
+ }
44
+ /**
45
+ * Tries to match a preset or a form with the provided value. Prioritizes presets over forms.
46
+ * @param dateFilterValue - value to match against
47
+ * @param availableOptions - date options available
48
+ * @alpha
49
+ */
50
+ export function matchDateFilterToDateFilterOption(dateFilter, availableOptions) {
51
+ // no value means common All Time, try matching against All time (if it is available) or create virtual preset for it
52
+ const isAllTime = isAllTimeDateFilter(dateFilter);
53
+ if (!dateFilter || isAllTime) {
54
+ const emptyValueHandling = dateFilter?.dateFilter.emptyValueHandling;
55
+ if (emptyValueHandling === "only") {
56
+ const { emptyValues: emptyValuesOption } = availableOptions;
57
+ if (emptyValuesOption?.visible) {
58
+ return {
59
+ dateFilterOption: { ...emptyValuesOption, emptyValueHandling: "only" },
60
+ excludeCurrentPeriod: false,
61
+ };
62
+ }
63
+ }
64
+ const { allTime } = availableOptions;
65
+ if (allTime) {
66
+ return {
67
+ dateFilterOption: applyEmptyValueHandling(allTime, dateFilter),
68
+ excludeCurrentPeriod: false,
69
+ };
70
+ }
71
+ const virtual = createVirtualPresetForStoredFilter(undefined);
72
+ return {
73
+ ...virtual,
74
+ dateFilterOption: applyEmptyValueHandling(virtual.dateFilterOption, dateFilter),
75
+ };
76
+ }
77
+ // try matching the filter as is
78
+ const matchingFilter = findDateFilterOptionByValue(dateFilter, availableOptions);
79
+ if (matchingFilter) {
80
+ return {
81
+ dateFilterOption: applyEmptyValueHandling(matchingFilter, dateFilter),
82
+ excludeCurrentPeriod: false,
83
+ };
84
+ }
85
+ // try matching the filter with excludeCurrentPeriod === true, but only for relativeFormPresets.
86
+ // Entry is gated on `revertExcludedCurrentPeriodRange` being the EXACT inverse of the filter bar's
87
+ // forward exclusion — so a genuine exclusion output (to === -1, from < -1) reconstructs its preset,
88
+ // but a plain "previous period" range (e.g. from === to === -1) is no longer force-matched as
89
+ // current+exclude the way the earlier `to === -1`-only check did.
90
+ if (dateFilter.dateFilter.type === "relative") {
91
+ const storedFrom = Number.parseInt(dateFilter.dateFilter.from?.toString() ?? "", 10);
92
+ const storedTo = Number.parseInt(dateFilter.dateFilter.to?.toString() ?? "", 10);
93
+ const unshifted = Number.isNaN(storedFrom) || Number.isNaN(storedTo)
94
+ ? undefined
95
+ : revertExcludedCurrentPeriodRange({ from: storedFrom, to: storedTo });
96
+ if (unshifted) {
97
+ const filterToMatch = {
98
+ dateFilter: { ...dateFilter.dateFilter, ...unshifted },
99
+ };
100
+ const matchingFilter = findDateFilterOptionByValueAndType(filterToMatch, availableOptions, "relativePreset");
101
+ if (matchingFilter) {
102
+ return {
103
+ dateFilterOption: applyEmptyValueHandling(matchingFilter, dateFilter),
104
+ excludeCurrentPeriod: true,
105
+ };
106
+ }
107
+ }
108
+ }
109
+ // the stored filter must be a form with custom values
110
+ if (canReconstructFormForStoredFilter(availableOptions, dateFilter)) {
111
+ return reconstructFormForStoredFilter(availableOptions, dateFilter);
112
+ }
113
+ // we cannot use the form because it is disabled or otherwise incompatible
114
+ // -> we must create a virtual hidden preset
115
+ const virtual = createVirtualPresetForStoredFilter(dateFilter);
116
+ return { ...virtual, dateFilterOption: applyEmptyValueHandling(virtual.dateFilterOption, dateFilter) };
117
+ }
118
+ /**
119
+ * Flattens the provided date filter options. The flattening maintains a stable, predefined order in which
120
+ * the options should be rendered by the date filter component.
121
+ *
122
+ * @param dateFilterOptions - available options to flatten
123
+ * @alpha
124
+ */
125
+ export function flattenDateFilterOptions(dateFilterOptions) {
126
+ // relativePreset is optional — guard it, or option sets without it (e.g. a static-only picker)
127
+ // crash the whole matching path on Object.values(undefined).
128
+ const relativePresets = compact(Object.values(dateFilterOptions.relativePreset ?? {})).flat();
129
+ // the order is significant here
130
+ // the first visible filter is selected for new dashboards if none is specified in config
131
+ return compact([
132
+ dateFilterOptions.allTime,
133
+ dateFilterOptions.emptyValues,
134
+ ...(dateFilterOptions.absolutePreset || []),
135
+ ...relativePresets,
136
+ dateFilterOptions.absoluteForm,
137
+ dateFilterOptions.relativeForm,
138
+ ]);
139
+ }
140
+ function canReconstructFormForStoredFilter(options, dateFilter) {
141
+ switch (dateFilter.dateFilter.type) {
142
+ case "absolute":
143
+ return isDateFilterOptionVisible(options.absoluteForm);
144
+ case "relative":
145
+ return isDateFilterOptionVisible(options.relativeForm);
146
+ default:
147
+ throw new Error("Unknown dateFilterValue type");
148
+ }
149
+ }
150
+ function reconstructFormForStoredFilter(options, dateFilter) {
151
+ if (dateFilter.dateFilter.type === "absolute") {
152
+ const dateFilterOption = {
153
+ ...options.absoluteForm,
154
+ from: dateFilter.dateFilter.from.toString(),
155
+ to: dateFilter.dateFilter.to.toString(),
156
+ type: "absoluteForm",
157
+ ...(dateFilter.dateFilter.emptyValueHandling
158
+ ? { emptyValueHandling: dateFilter.dateFilter.emptyValueHandling }
159
+ : {}),
160
+ };
161
+ return { dateFilterOption, excludeCurrentPeriod: false };
162
+ }
163
+ else {
164
+ const dateFilterOption = {
165
+ ...options.relativeForm,
166
+ from: Number.parseInt(dateFilter.dateFilter.from.toString(), 10),
167
+ to: Number.parseInt(dateFilter.dateFilter.to.toString(), 10),
168
+ granularity: dateFilter.dateFilter.granularity,
169
+ type: "relativeForm",
170
+ ...(dateFilter.dateFilter.emptyValueHandling
171
+ ? { emptyValueHandling: dateFilter.dateFilter.emptyValueHandling }
172
+ : {}),
173
+ };
174
+ return { dateFilterOption, excludeCurrentPeriod: false };
175
+ }
176
+ }
177
+ function isDateFilterOptionVisible(option) {
178
+ return !!option?.visible;
179
+ }
180
+ function findDateFilterOptionById(id, dateFilterOptions) {
181
+ return flattenDateFilterOptions(dateFilterOptions).find((option) => option.localIdentifier === id);
182
+ }
183
+ /**
184
+ * Finds the date filter option whose stored value equals the provided filter's value.
185
+ *
186
+ * @alpha
187
+ */
188
+ export function findDateFilterOptionByValue(dateFilter, dateFilterOptions) {
189
+ return flattenDateFilterOptions(dateFilterOptions).find(filterMatchesData(dateFilter));
190
+ }
191
+ function findDateFilterOptionByValueAndType(dateFilter, dateFilterOptions, type) {
192
+ return flattenDateFilterOptions(dateFilterOptions)
193
+ .filter((option) => option.type === type)
194
+ .find(filterMatchesData(dateFilter));
195
+ }
196
+ const filterMatchesData = (dateFilter) => (filter) => {
197
+ if (!filter) {
198
+ return false;
199
+ }
200
+ const data = dateFilter.dateFilter;
201
+ if (isAbsoluteDateFilterOption(filter)) {
202
+ return data.type === "absolute" && filter.from === data.from && filter.to === data.to;
203
+ }
204
+ if (isRelativeDateFilterOption(filter)) {
205
+ // Check if boundedFilter properties match exactly
206
+ const boundedFiltersMatch = (() => {
207
+ // If both don't have boundedFilter, they match
208
+ if (!filter.boundedFilter && !data.boundedFilter) {
209
+ return true;
210
+ }
211
+ // If only one has boundedFilter, they don't match
212
+ if (!filter.boundedFilter || !data.boundedFilter) {
213
+ return false;
214
+ }
215
+ // Both have boundedFilter - check granularity first
216
+ if (filter.boundedFilter.granularity !== data.boundedFilter.granularity) {
217
+ return false;
218
+ }
219
+ // Both must be the same type (both lower or both upper)
220
+ if (isLowerBound(filter.boundedFilter) && isLowerBound(data.boundedFilter)) {
221
+ return filter.boundedFilter.from === data.boundedFilter.from;
222
+ }
223
+ else if (isUpperBound(filter.boundedFilter) && isUpperBound(data.boundedFilter)) {
224
+ return filter.boundedFilter.to === data.boundedFilter.to;
225
+ }
226
+ // Different bound types don't match
227
+ return false;
228
+ })();
229
+ return (data.type === "relative" &&
230
+ filter.from?.toString() === data.from?.toString() &&
231
+ filter.to?.toString() === data.to?.toString() &&
232
+ filter.granularity === data.granularity &&
233
+ boundedFiltersMatch);
234
+ }
235
+ return false;
236
+ };
237
+ /**
238
+ * Creates a virtual preset with values corresponding to a provided server-side value.
239
+ * This is used in situations when a dashboard was saved with setting
240
+ * that can no longer be reproduced by the available options (e.g. relativeForm was used and later, was disabled)
241
+ */
242
+ function createVirtualPresetForStoredFilter(dateFilter) {
243
+ if (!dateFilter) {
244
+ const dateFilterOption = {
245
+ ...virtualPresetBase,
246
+ type: "allTime",
247
+ };
248
+ return { dateFilterOption, excludeCurrentPeriod: false };
249
+ }
250
+ const { type, from, to, granularity } = dateFilter.dateFilter;
251
+ if (type === "absolute") {
252
+ const dateFilterOption = {
253
+ ...virtualPresetBase,
254
+ from: from.toString(),
255
+ to: to.toString(),
256
+ type: "absolutePreset",
257
+ ...(dateFilter.dateFilter.emptyValueHandling
258
+ ? { emptyValueHandling: dateFilter.dateFilter.emptyValueHandling }
259
+ : {}),
260
+ };
261
+ return { dateFilterOption, excludeCurrentPeriod: false };
262
+ }
263
+ else {
264
+ const dateFilterOption = {
265
+ ...virtualPresetBase,
266
+ from: Number.parseInt(from.toString(), 10),
267
+ to: Number.parseInt(to.toString(), 10),
268
+ granularity,
269
+ type: "relativePreset",
270
+ ...(dateFilter.dateFilter.boundedFilter
271
+ ? { boundedFilter: dateFilter.dateFilter.boundedFilter }
272
+ : {}),
273
+ ...(dateFilter.dateFilter.emptyValueHandling
274
+ ? { emptyValueHandling: dateFilter.dateFilter.emptyValueHandling }
275
+ : {}),
276
+ };
277
+ return { dateFilterOption, excludeCurrentPeriod: false };
278
+ }
279
+ }
@@ -0,0 +1,22 @@
1
+ import { type IDateFilterConfig } from "@gooddata/sdk-model";
2
+ /**
3
+ * Default date filter preset for standard calendar.
4
+ *
5
+ * @alpha
6
+ */
7
+ export declare const DEFAULT_DATE_FILTER_PRESET = "THIS_MONTH";
8
+ /**
9
+ * Default date filter preset for fiscal calendar.
10
+ * Used when fiscal calendar is set as the default calendar type.
11
+ *
12
+ * @alpha
13
+ */
14
+ export declare const DEFAULT_FISCAL_DATE_FILTER_PRESET = "THIS_FISCAL_MONTH";
15
+ /**
16
+ * The date filter configuration used when a workspace defines no (valid) custom one — the same
17
+ * catalog the dashboard date filter falls back to.
18
+ *
19
+ * @alpha
20
+ */
21
+ export declare const defaultDateFilterConfig: IDateFilterConfig;
22
+ //# sourceMappingURL=DefaultDateFilterConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultDateFilterConfig.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/utils/DefaultDateFilterConfig.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,iBAAiB,EAAS,MAAM,qBAAqB,CAAC;AAEpE;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,eAAe,CAAC;AAEvD;;;;;GAKG;AACH,eAAO,MAAM,iCAAiC,sBAAsB,CAAC;AAErE;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,iBAyarC,CAAC"}