@gooddata/sdk-ui-filters 11.54.0-alpha.0 → 11.54.0-alpha.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.
@@ -1,4 +1,4 @@
1
- import { type ComponentType, PureComponent, type ReactNode } from "react";
1
+ import { type ComponentType, type ReactNode } from "react";
2
2
  import { type DateFilterGranularity, type IActiveCalendars, type WeekStart } from "@gooddata/sdk-model";
3
3
  import { type OverlayPositionType } from "@gooddata/sdk-ui-kit";
4
4
  import { type IFilterButtonCustomIcon, type VisibilityMode } from "../shared/interfaces/index.js";
@@ -169,26 +169,5 @@ export interface IDateFilterState extends IDateFilterStatePropsIntersection {
169
169
  *
170
170
  * @public
171
171
  */
172
- export declare class DateFilter extends PureComponent<IDateFilterProps, IDateFilterState> {
173
- static defaultProps: Partial<IDateFilterProps>;
174
- static getDerivedStateFromProps(nextProps: IDateFilterProps, prevState: IDateFilterState): IDateFilterState | null;
175
- private static getStateFromProps;
176
- private static getStateFromWorkingProps;
177
- private static getStateFromSelectedOption;
178
- private static checkInitialFilterOption;
179
- constructor(props: IDateFilterProps);
180
- componentDidMount(): void;
181
- render(): import("react/jsx-runtime").JSX.Element | null;
182
- private handleApplyClick;
183
- private onChangesDiscarded;
184
- private onCancelClicked;
185
- private onDropdownOpenChanged;
186
- private handleExcludeCurrentPeriodChange;
187
- private handleSelectedFilterOptionChange;
188
- private handleSelectChange;
189
- private fireOnSelect;
190
- private normalizeEmptyValueHandling;
191
- private mergeEmptyValueHandling;
192
- private isSameOptionExceptEmptyValueHandling;
193
- }
172
+ export declare const DateFilter: import("react").NamedExoticComponent<IDateFilterProps>;
194
173
  //# sourceMappingURL=DateFilter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DateFilter.d.ts","sourceRoot":"","sources":["../../src/DateFilter/DateFilter.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DateFilter.d.ts","sourceRoot":"","sources":["../../src/DateFilter/DateFilter.tsx"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  // (C) 2007-2026 GoodData Corporation
3
- import { PureComponent } from "react";
3
+ import { memo, useCallback, useEffect, useRef, useState } from "react";
4
4
  import { isEmpty, isEqual } from "lodash-es";
5
5
  import { isAbsoluteDateFilterForm, isAllTimeDateFilterOption, isEmptyValuesDateFilterOption, } from "@gooddata/sdk-model";
6
6
  import { DEFAULT_DATE_FORMAT } from "./constants/Platform.js";
@@ -9,149 +9,125 @@ import { isUiRelativeDateFilterForm, } from "./interfaces/index.js";
9
9
  import { normalizeSelectedFilterOption } from "./utils/FilterOptionNormalization.js";
10
10
  import { canExcludeCurrentPeriod } from "./utils/PeriodExclusion.js";
11
11
  import { validateFilterOption } from "./validation/OptionValidation.js";
12
- /**
13
- * {@link https://www.gooddata.com/docs/gooddata-ui/latest/references/filters/date_filter | DateFilter} is a component for configuring a date filter value.
14
- *
15
- * @public
16
- */
17
- export class DateFilter extends PureComponent {
18
- static defaultProps = {
19
- dateFormat: DEFAULT_DATE_FORMAT,
20
- isEditMode: false,
21
- isTimeForAbsoluteRangeEnabled: false,
22
- isSecondsForAbsoluteRangeEnabled: false,
23
- locale: "en-US",
24
- onCancel: () => { },
25
- onOpen: () => { },
26
- onClose: () => { },
27
- weekStart: "Sunday",
28
- withoutApply: false,
29
- hideDisabledExclude: false,
12
+ function getStateFromProps(props) {
13
+ const canExcludeCurrent = canExcludeCurrentPeriod(props.selectedFilterOption);
14
+ return {
15
+ initSelectedFilterOption: props.selectedFilterOption,
16
+ selectedFilterOption: props.selectedFilterOption,
17
+ initExcludeCurrentPeriod: props.excludeCurrentPeriod,
18
+ excludeCurrentPeriod: canExcludeCurrent ? props.excludeCurrentPeriod : false,
19
+ isExcludeCurrentPeriodEnabled: canExcludeCurrent,
20
+ initWorkingSelectedFilterOption: props.workingSelectedFilterOption ?? props.selectedFilterOption,
21
+ initWorkingExcludeCurrentPeriod: props.workingExcludeCurrentPeriod ?? props.excludeCurrentPeriod,
30
22
  };
31
- static getDerivedStateFromProps(nextProps, prevState) {
32
- if (nextProps.withoutApply &&
33
- nextProps.workingSelectedFilterOption &&
34
- nextProps.excludeCurrentPeriod !== undefined &&
35
- (!isEqual(nextProps.workingSelectedFilterOption, prevState.initWorkingSelectedFilterOption) ||
36
- nextProps.workingExcludeCurrentPeriod !== prevState.initWorkingExcludeCurrentPeriod)) {
37
- return DateFilter.getStateFromWorkingProps(nextProps);
38
- }
39
- if (!isEqual(nextProps.selectedFilterOption, prevState.initSelectedFilterOption) ||
40
- nextProps.excludeCurrentPeriod !== prevState.initExcludeCurrentPeriod) {
41
- return DateFilter.getStateFromProps(nextProps);
42
- }
43
- return null;
44
- }
45
- static getStateFromProps(props) {
46
- const canExcludeCurrent = canExcludeCurrentPeriod(props.selectedFilterOption);
47
- return {
48
- initSelectedFilterOption: props.selectedFilterOption,
49
- selectedFilterOption: props.selectedFilterOption,
50
- initExcludeCurrentPeriod: props.excludeCurrentPeriod,
51
- excludeCurrentPeriod: canExcludeCurrent ? props.excludeCurrentPeriod : false,
52
- isExcludeCurrentPeriodEnabled: canExcludeCurrent,
53
- initWorkingSelectedFilterOption: props.workingSelectedFilterOption ?? props.selectedFilterOption,
54
- initWorkingExcludeCurrentPeriod: props.workingExcludeCurrentPeriod ?? props.excludeCurrentPeriod,
55
- };
56
- }
57
- static getStateFromWorkingProps(props) {
58
- const selectedFilterOption = props.workingSelectedFilterOption ?? props.selectedFilterOption;
59
- const canExcludeCurrent = canExcludeCurrentPeriod(selectedFilterOption);
60
- return {
61
- ...DateFilter.getStateFromProps(props),
62
- selectedFilterOption: selectedFilterOption,
63
- excludeCurrentPeriod: canExcludeCurrent
64
- ? (props.workingExcludeCurrentPeriod ?? props.excludeCurrentPeriod)
65
- : false,
66
- initWorkingExcludeCurrentPeriod: props.workingExcludeCurrentPeriod ?? props.excludeCurrentPeriod,
67
- initWorkingSelectedFilterOption: selectedFilterOption,
68
- initExcludeCurrentPeriod: props.excludeCurrentPeriod,
69
- isExcludeCurrentPeriodEnabled: canExcludeCurrent,
70
- };
71
- }
72
- static getStateFromSelectedOption = (selectedFilterOption, excludeCurrentPeriod) => {
73
- const canExcludeCurrent = canExcludeCurrentPeriod(selectedFilterOption);
74
- return {
75
- selectedFilterOption,
76
- excludeCurrentPeriod: canExcludeCurrent ? excludeCurrentPeriod : false,
77
- isExcludeCurrentPeriodEnabled: canExcludeCurrent,
78
- };
23
+ }
24
+ function getStateFromWorkingProps(props) {
25
+ const selectedFilterOption = props.workingSelectedFilterOption ?? props.selectedFilterOption;
26
+ const canExcludeCurrent = canExcludeCurrentPeriod(selectedFilterOption);
27
+ return {
28
+ ...getStateFromProps(props),
29
+ selectedFilterOption: selectedFilterOption,
30
+ excludeCurrentPeriod: canExcludeCurrent
31
+ ? (props.workingExcludeCurrentPeriod ?? props.excludeCurrentPeriod)
32
+ : false,
33
+ initWorkingExcludeCurrentPeriod: props.workingExcludeCurrentPeriod ?? props.excludeCurrentPeriod,
34
+ initWorkingSelectedFilterOption: selectedFilterOption,
35
+ initExcludeCurrentPeriod: props.excludeCurrentPeriod,
36
+ isExcludeCurrentPeriodEnabled: canExcludeCurrent,
79
37
  };
80
- static checkInitialFilterOption = (filterOption) => {
81
- if (isAbsoluteDateFilterForm(filterOption) &&
82
- (filterOption.from === null ||
83
- filterOption.from === undefined ||
84
- filterOption.to === null ||
85
- filterOption.to === undefined)) {
86
- console.warn("The default filter option is not valid. Values 'from' and 'to' from absoluteForm filter option must be specified.");
87
- }
88
- if (isUiRelativeDateFilterForm(filterOption) &&
89
- (filterOption.from === null ||
90
- filterOption.from === undefined ||
91
- filterOption.to === null ||
92
- filterOption.to === undefined)) {
93
- console.warn("The default filter option is not valid. Values 'from' and 'to' from relativeForm filter option must be specified.");
94
- }
38
+ }
39
+ function getStateFromSelectedOption(selectedFilterOption, excludeCurrentPeriod) {
40
+ const canExcludeCurrent = canExcludeCurrentPeriod(selectedFilterOption);
41
+ return {
42
+ selectedFilterOption,
43
+ excludeCurrentPeriod: canExcludeCurrent ? excludeCurrentPeriod : false,
44
+ isExcludeCurrentPeriodEnabled: canExcludeCurrent,
95
45
  };
96
- constructor(props) {
97
- super(props);
98
- this.state = DateFilter.getStateFromProps(props);
46
+ }
47
+ /**
48
+ * Equivalent of the former `getDerivedStateFromProps`: re-syncs the state whenever the incoming
49
+ * props no longer match the `init*` sentinels stored in the state. Returns `null` when the state
50
+ * is already in sync.
51
+ */
52
+ function getDerivedStateFromProps(nextProps, prevState) {
53
+ if (nextProps.withoutApply &&
54
+ nextProps.workingSelectedFilterOption &&
55
+ nextProps.excludeCurrentPeriod !== undefined &&
56
+ (!isEqual(nextProps.workingSelectedFilterOption, prevState.initWorkingSelectedFilterOption) ||
57
+ nextProps.workingExcludeCurrentPeriod !== prevState.initWorkingExcludeCurrentPeriod)) {
58
+ return getStateFromWorkingProps(nextProps);
99
59
  }
100
- componentDidMount() {
101
- DateFilter.checkInitialFilterOption(this.props.selectedFilterOption);
60
+ if (!isEqual(nextProps.selectedFilterOption, prevState.initSelectedFilterOption) ||
61
+ nextProps.excludeCurrentPeriod !== prevState.initExcludeCurrentPeriod) {
62
+ return getStateFromProps(nextProps);
102
63
  }
103
- render() {
104
- const { customFilterName, dateFilterMode, dateFormat, filterOptions, selectedFilterOption: originalSelectedFilterOption, excludeCurrentPeriod: originalExcludeCurrentPeriod, availableGranularities, isEditMode, openOnInit, locale, isTimeForAbsoluteRangeEnabled, isSecondsForAbsoluteRangeEnabled, weekStart, customIcon, showDropDownHeaderMessage, FilterConfigurationComponent, withoutApply, ButtonComponent, overlayPositionType, activeCalendars, enableEmptyDateValues, customRangeHint, } = this.props;
105
- const { excludeCurrentPeriod, selectedFilterOption, isExcludeCurrentPeriodEnabled } = this.state;
106
- return dateFilterMode === "hidden" ? null : (_jsx(DateFilterCore, { availableGranularities: availableGranularities, customFilterName: customFilterName, dateFormat: dateFormat ?? DEFAULT_DATE_FORMAT, openOnInit: openOnInit, showDropDownHeaderMessage: showDropDownHeaderMessage, disabled: dateFilterMode === "readonly", excludeCurrentPeriod: excludeCurrentPeriod, originalExcludeCurrentPeriod: originalExcludeCurrentPeriod, isExcludeCurrentPeriodEnabled: isExcludeCurrentPeriodEnabled, hideDisabledExclude: this.props.hideDisabledExclude, isTimeForAbsoluteRangeEnabled: isTimeForAbsoluteRangeEnabled ?? false, isSecondsForAbsoluteRangeEnabled: isSecondsForAbsoluteRangeEnabled ?? false, isEditMode: isEditMode ?? false, filterOptions: filterOptions, selectedFilterOption: selectedFilterOption, originalSelectedFilterOption: originalSelectedFilterOption, locale: locale, onApplyClick: this.handleApplyClick, onCancelClick: this.onCancelClicked, onDropdownOpenChanged: this.onDropdownOpenChanged, onExcludeCurrentPeriodChange: this.handleExcludeCurrentPeriodChange, onSelectedFilterOptionChange: this.handleSelectedFilterOptionChange, errors: validateFilterOption(selectedFilterOption), weekStart: weekStart, customIcon: customIcon, FilterConfigurationComponent: FilterConfigurationComponent, withoutApply: withoutApply, ButtonComponent: ButtonComponent, overlayPositionType: overlayPositionType, activeCalendars: activeCalendars, enableEmptyDateValues: enableEmptyDateValues, customRangeHint: customRangeHint }));
64
+ return null;
65
+ }
66
+ function checkInitialFilterOption(filterOption) {
67
+ if (isAbsoluteDateFilterForm(filterOption) &&
68
+ (filterOption.from === null ||
69
+ filterOption.from === undefined ||
70
+ filterOption.to === null ||
71
+ filterOption.to === undefined)) {
72
+ console.warn("The default filter option is not valid. Values 'from' and 'to' from absoluteForm filter option must be specified.");
107
73
  }
108
- handleApplyClick = () => {
109
- const normalizedSelectedFilterOption = this.normalizeEmptyValueHandling(normalizeSelectedFilterOption(this.state.selectedFilterOption));
110
- this.props.onApply(normalizedSelectedFilterOption, this.state.excludeCurrentPeriod);
111
- };
112
- onChangesDiscarded = () => {
113
- if (!this.props.withoutApply) {
114
- this.setState(() => DateFilter.getStateFromProps(this.props), this.handleSelectChange);
115
- }
116
- else if (this.props.withoutApply &&
117
- !isEmpty(validateFilterOption(this.state.selectedFilterOption))) {
118
- this.setState(() => DateFilter.getStateFromWorkingProps(this.props));
119
- }
120
- };
121
- onCancelClicked = () => {
122
- this.props.onCancel?.();
123
- this.onChangesDiscarded();
124
- };
125
- onDropdownOpenChanged = (isOpen) => {
126
- if (isOpen) {
127
- this.props.onOpen?.();
128
- }
129
- else {
130
- this.props.onClose?.();
131
- this.onChangesDiscarded();
132
- }
133
- };
134
- handleExcludeCurrentPeriodChange = (excludeCurrentPeriod) => {
135
- this.setState({ excludeCurrentPeriod }, this.handleSelectChange);
136
- };
137
- handleSelectedFilterOptionChange = (selectedFilterOption) => {
138
- let nextSelectedFilterOption;
139
- this.setState((state) => {
140
- nextSelectedFilterOption = this.normalizeEmptyValueHandling(this.mergeEmptyValueHandling(selectedFilterOption, state.selectedFilterOption));
141
- return DateFilter.getStateFromSelectedOption(nextSelectedFilterOption, state.excludeCurrentPeriod);
142
- }, () => this.fireOnSelect(nextSelectedFilterOption));
143
- };
144
- handleSelectChange = () => {
145
- this.fireOnSelect();
146
- };
147
- fireOnSelect(selectedFilterOption = this.state.selectedFilterOption, excludeCurrentPeriod = this.state.excludeCurrentPeriod) {
148
- const normalizedSelectedFilterOption = this.normalizeEmptyValueHandling(normalizeSelectedFilterOption(selectedFilterOption));
149
- if (isEmpty(validateFilterOption(normalizedSelectedFilterOption))) {
150
- this.props.onSelect?.(normalizedSelectedFilterOption, excludeCurrentPeriod);
151
- }
74
+ if (isUiRelativeDateFilterForm(filterOption) &&
75
+ (filterOption.from === null ||
76
+ filterOption.from === undefined ||
77
+ filterOption.to === null ||
78
+ filterOption.to === undefined)) {
79
+ console.warn("The default filter option is not valid. Values 'from' and 'to' from relativeForm filter option must be specified.");
152
80
  }
153
- normalizeEmptyValueHandling = (selectedFilterOption) => {
154
- if (!this.props.enableEmptyDateValues) {
81
+ }
82
+ function isSameOptionExceptEmptyValueHandling(a, b) {
83
+ const { emptyValueHandling: _aEmptyValueHandling, ...aWithoutEmptyValueHandling } = a;
84
+ const { emptyValueHandling: _bEmptyValueHandling, ...bWithoutEmptyValueHandling } = b;
85
+ return isEqual(aWithoutEmptyValueHandling, bWithoutEmptyValueHandling);
86
+ }
87
+ /**
88
+ * {@link https://www.gooddata.com/docs/gooddata-ui/latest/references/filters/date_filter | DateFilter} is a component for configuring a date filter value.
89
+ *
90
+ * @public
91
+ */
92
+ export const DateFilter = memo(function DateFilter(props) {
93
+ const { customFilterName, dateFilterMode, dateFormat = DEFAULT_DATE_FORMAT, filterOptions, selectedFilterOption: originalSelectedFilterOption, excludeCurrentPeriod: originalExcludeCurrentPeriod, availableGranularities, isEditMode = false, openOnInit, locale = "en-US", isTimeForAbsoluteRangeEnabled = false, isSecondsForAbsoluteRangeEnabled = false, weekStart = "Sunday", customIcon, showDropDownHeaderMessage, FilterConfigurationComponent, withoutApply = false, ButtonComponent, overlayPositionType, activeCalendars, enableEmptyDateValues, customRangeHint, hideDisabledExclude = false, onApply, onSelect, onCancel = () => { }, onOpen = () => { }, onClose = () => { }, } = props;
94
+ const [storedState, setStoredState] = useState(() => getStateFromProps(props));
95
+ // The class component read `this.state` at call time, so a handler invoked later than the
96
+ // render that produced it still saw the current state. Handlers of a function component
97
+ // capture their render instead, and the range picker defers the Enter submit by a tick (see
98
+ // `updateRangeState` in DateRangePicker) - the deferred `submitForm` therefore calls the
99
+ // `onApplyClick` of the render that scheduled it, i.e. from before the typed date was stored.
100
+ // Mirroring the state in a ref and updating it together with the state restores the original
101
+ // semantics for such deferred calls.
102
+ const stateRef = useRef(storedState);
103
+ const setState = useCallback((updater) => {
104
+ stateRef.current = updater(stateRef.current);
105
+ setStoredState(stateRef.current);
106
+ }, []);
107
+ // The class fired `onSelect` from a `setState` callback, i.e. only after the new state was
108
+ // committed and re-synced from the (possibly already updated) props. Firing it straight from
109
+ // the handler would emit the pre-commit option - most visibly on Apply, where the body runs
110
+ // `onApplyClick(); closeDropdown();` in a single batch, so the discard triggered by closing
111
+ // would report the option that Apply has just replaced. Handlers therefore only mark a
112
+ // pending emission here and the effect below fires it once the state has settled.
113
+ // `selectedFilterOption` is set when the handler already knows the option to report.
114
+ const pendingSelectRef = useRef(null);
115
+ // Replacement of the former `getDerivedStateFromProps`: the state is adjusted during render
116
+ // (see https://react.dev/reference/react/useState#storing-information-from-previous-renders)
117
+ // so that the re-synced option is rendered right away instead of flashing the stale one.
118
+ // The `isEqual` guard makes the extra render pass a no-op once the state is in sync.
119
+ let state = storedState;
120
+ const derivedState = getDerivedStateFromProps(props, storedState);
121
+ if (derivedState !== null && !isEqual(derivedState, storedState)) {
122
+ setState(() => derivedState);
123
+ state = derivedState;
124
+ }
125
+ useEffect(() => {
126
+ checkInitialFilterOption(originalSelectedFilterOption);
127
+ // eslint-disable-next-line react-hooks/exhaustive-deps
128
+ }, []);
129
+ const normalizeEmptyValueHandling = (selectedFilterOption) => {
130
+ if (!enableEmptyDateValues) {
155
131
  return selectedFilterOption;
156
132
  }
157
133
  if (isEmptyValuesDateFilterOption(selectedFilterOption)) {
@@ -168,8 +144,8 @@ export class DateFilter extends PureComponent {
168
144
  }
169
145
  return selectedFilterOption;
170
146
  };
171
- mergeEmptyValueHandling = (nextSelectedFilterOption, currentSelectedFilterOption) => {
172
- if (!this.props.enableEmptyDateValues) {
147
+ const mergeEmptyValueHandling = (nextSelectedFilterOption, currentSelectedFilterOption) => {
148
+ if (!enableEmptyDateValues) {
173
149
  return nextSelectedFilterOption;
174
150
  }
175
151
  if (isEmptyValuesDateFilterOption(nextSelectedFilterOption)) {
@@ -189,7 +165,7 @@ export class DateFilter extends PureComponent {
189
165
  currentHandling !== "only") {
190
166
  // If the only difference is `emptyValueHandling` (e.g. user unchecks the checkbox),
191
167
  // do not carry the previous value back in.
192
- if (this.isSameOptionExceptEmptyValueHandling(nextSelectedFilterOption, currentSelectedFilterOption)) {
168
+ if (isSameOptionExceptEmptyValueHandling(nextSelectedFilterOption, currentSelectedFilterOption)) {
193
169
  return nextSelectedFilterOption;
194
170
  }
195
171
  return {
@@ -199,9 +175,68 @@ export class DateFilter extends PureComponent {
199
175
  }
200
176
  return nextSelectedFilterOption;
201
177
  };
202
- isSameOptionExceptEmptyValueHandling = (a, b) => {
203
- const { emptyValueHandling: _aEmptyValueHandling, ...aWithoutEmptyValueHandling } = a;
204
- const { emptyValueHandling: _bEmptyValueHandling, ...bWithoutEmptyValueHandling } = b;
205
- return isEqual(aWithoutEmptyValueHandling, bWithoutEmptyValueHandling);
178
+ const fireOnSelect = (selectedFilterOption, excludeCurrentPeriod) => {
179
+ const normalizedSelectedFilterOption = normalizeEmptyValueHandling(normalizeSelectedFilterOption(selectedFilterOption));
180
+ if (isEmpty(validateFilterOption(normalizedSelectedFilterOption))) {
181
+ onSelect?.(normalizedSelectedFilterOption, excludeCurrentPeriod);
182
+ }
206
183
  };
207
- }
184
+ // Runs after every commit, i.e. after the derived-state block above has re-synced `state` with
185
+ // the incoming props - the equivalent of the former `setState(updater, callback)` timing.
186
+ useEffect(() => {
187
+ const pendingSelect = pendingSelectRef.current;
188
+ if (!pendingSelect) {
189
+ return;
190
+ }
191
+ pendingSelectRef.current = null;
192
+ fireOnSelect(pendingSelect.selectedFilterOption ?? state.selectedFilterOption, state.excludeCurrentPeriod);
193
+ });
194
+ const handleApplyClick = () => {
195
+ // `stateRef`, not `state`: this can run from the range picker's deferred submit.
196
+ const { selectedFilterOption: optionToApply, excludeCurrentPeriod: excludeToApply } = stateRef.current;
197
+ const normalizedSelectedFilterOption = normalizeEmptyValueHandling(normalizeSelectedFilterOption(optionToApply));
198
+ onApply(normalizedSelectedFilterOption, excludeToApply);
199
+ };
200
+ const onChangesDiscarded = () => {
201
+ if (!withoutApply) {
202
+ setState(() => getStateFromProps(props));
203
+ pendingSelectRef.current = {};
204
+ }
205
+ else if (withoutApply && !isEmpty(validateFilterOption(stateRef.current.selectedFilterOption))) {
206
+ setState(() => getStateFromWorkingProps(props));
207
+ }
208
+ };
209
+ const onCancelClicked = () => {
210
+ onCancel();
211
+ onChangesDiscarded();
212
+ };
213
+ const onDropdownOpenChanged = (isOpen) => {
214
+ if (isOpen) {
215
+ onOpen();
216
+ }
217
+ else {
218
+ onClose();
219
+ onChangesDiscarded();
220
+ }
221
+ };
222
+ const handleExcludeCurrentPeriodChange = (excludeCurrentPeriod) => {
223
+ setState((prevState) => ({ ...prevState, excludeCurrentPeriod }));
224
+ pendingSelectRef.current = {};
225
+ };
226
+ const handleSelectedFilterOptionChange = (selectedFilterOption) => {
227
+ setState((prevState) => {
228
+ // Merging against `prevState` rather than the render snapshot mirrors the former
229
+ // updater: two option changes batched into one event must both be taken into account.
230
+ const nextSelectedFilterOption = normalizeEmptyValueHandling(mergeEmptyValueHandling(selectedFilterOption, prevState.selectedFilterOption));
231
+ // Recorded here - as the former updater did with its closure variable - so that the
232
+ // effect above reports the option this change actually resolved to.
233
+ pendingSelectRef.current = { selectedFilterOption: nextSelectedFilterOption };
234
+ return {
235
+ ...prevState,
236
+ ...getStateFromSelectedOption(nextSelectedFilterOption, prevState.excludeCurrentPeriod),
237
+ };
238
+ });
239
+ };
240
+ const { excludeCurrentPeriod, selectedFilterOption, isExcludeCurrentPeriodEnabled } = state;
241
+ return dateFilterMode === "hidden" ? null : (_jsx(DateFilterCore, { availableGranularities: availableGranularities, customFilterName: customFilterName, dateFormat: dateFormat ?? DEFAULT_DATE_FORMAT, openOnInit: openOnInit, showDropDownHeaderMessage: showDropDownHeaderMessage, disabled: dateFilterMode === "readonly", excludeCurrentPeriod: excludeCurrentPeriod, originalExcludeCurrentPeriod: originalExcludeCurrentPeriod, isExcludeCurrentPeriodEnabled: isExcludeCurrentPeriodEnabled, hideDisabledExclude: hideDisabledExclude, isTimeForAbsoluteRangeEnabled: isTimeForAbsoluteRangeEnabled ?? false, isSecondsForAbsoluteRangeEnabled: isSecondsForAbsoluteRangeEnabled ?? false, isEditMode: isEditMode ?? false, filterOptions: filterOptions, selectedFilterOption: selectedFilterOption, originalSelectedFilterOption: originalSelectedFilterOption, locale: locale, onApplyClick: handleApplyClick, onCancelClick: onCancelClicked, onDropdownOpenChanged: onDropdownOpenChanged, onExcludeCurrentPeriodChange: handleExcludeCurrentPeriodChange, onSelectedFilterOptionChange: handleSelectedFilterOptionChange, errors: validateFilterOption(selectedFilterOption), weekStart: weekStart, customIcon: customIcon, FilterConfigurationComponent: FilterConfigurationComponent, withoutApply: withoutApply, ButtonComponent: ButtonComponent, overlayPositionType: overlayPositionType, activeCalendars: activeCalendars, enableEmptyDateValues: enableEmptyDateValues, customRangeHint: customRangeHint }));
242
+ });
@@ -61,7 +61,6 @@ import { NamedExoticComponent } from 'react';
61
61
  import { ObjRef } from '@gooddata/sdk-model';
62
62
  import { ObjRefInScope } from '@gooddata/sdk-model';
63
63
  import { OverlayPositionType } from '@gooddata/sdk-ui-kit';
64
- import { PureComponent } from 'react';
65
64
  import { RangeConditionOperator } from '@gooddata/sdk-model';
66
65
  import { ReactElement } from 'react';
67
66
  import { ReactNode } from 'react';
@@ -513,28 +512,7 @@ export declare function createFilterFromOperator(operator: TextFilterOperator, v
513
512
  *
514
513
  * @public
515
514
  */
516
- export declare class DateFilter extends PureComponent<IDateFilterProps, IDateFilterState> {
517
- static defaultProps: Partial<IDateFilterProps>;
518
- static getDerivedStateFromProps(nextProps: IDateFilterProps, prevState: IDateFilterState): IDateFilterState | null;
519
- private static getStateFromProps;
520
- private static getStateFromWorkingProps;
521
- private static getStateFromSelectedOption;
522
- private static checkInitialFilterOption;
523
- constructor(props: IDateFilterProps);
524
- componentDidMount(): void;
525
- render(): JSX.Element | null;
526
- private handleApplyClick;
527
- private onChangesDiscarded;
528
- private onCancelClicked;
529
- private onDropdownOpenChanged;
530
- private handleExcludeCurrentPeriodChange;
531
- private handleSelectedFilterOptionChange;
532
- private handleSelectChange;
533
- private fireOnSelect;
534
- private normalizeEmptyValueHandling;
535
- private mergeEmptyValueHandling;
536
- private isSameOptionExceptEmptyValueHandling;
537
- }
515
+ export declare const DateFilter: NamedExoticComponent<IDateFilterProps>;
538
516
 
539
517
  /**
540
518
  * @beta
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gooddata/sdk-ui-filters",
3
- "version": "11.54.0-alpha.0",
3
+ "version": "11.54.0-alpha.2",
4
4
  "description": "GoodData.UI SDK - Filter Components",
5
5
  "license": "MIT",
6
6
  "author": "GoodData Corporation",
@@ -46,11 +46,11 @@
46
46
  "ts-invariant": "0.10.3",
47
47
  "tslib": "2.8.1",
48
48
  "uuid": "11.1.1",
49
- "@gooddata/sdk-backend-spi": "11.54.0-alpha.0",
50
- "@gooddata/sdk-model": "11.54.0-alpha.0",
51
- "@gooddata/sdk-ui": "11.54.0-alpha.0",
52
- "@gooddata/sdk-ui-kit": "11.54.0-alpha.0",
53
- "@gooddata/util": "11.54.0-alpha.0"
49
+ "@gooddata/sdk-backend-spi": "11.54.0-alpha.2",
50
+ "@gooddata/sdk-model": "11.54.0-alpha.2",
51
+ "@gooddata/sdk-ui": "11.54.0-alpha.2",
52
+ "@gooddata/util": "11.54.0-alpha.2",
53
+ "@gooddata/sdk-ui-kit": "11.54.0-alpha.2"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@microsoft/api-documenter": "^7.17.0",
@@ -92,12 +92,12 @@
92
92
  "typescript": "5.9.3",
93
93
  "vitest": "4.1.8",
94
94
  "vitest-dom": "0.1.1",
95
- "@gooddata/eslint-config": "11.54.0-alpha.0",
96
- "@gooddata/oxlint-config": "11.54.0-alpha.0",
97
- "@gooddata/reference-workspace": "11.54.0-alpha.0",
98
- "@gooddata/sdk-backend-mockingbird": "11.54.0-alpha.0",
99
- "@gooddata/stylelint-config": "11.54.0-alpha.0",
100
- "@gooddata/sdk-ui-theme-provider": "11.54.0-alpha.0"
95
+ "@gooddata/eslint-config": "11.54.0-alpha.2",
96
+ "@gooddata/reference-workspace": "11.54.0-alpha.2",
97
+ "@gooddata/oxlint-config": "11.54.0-alpha.2",
98
+ "@gooddata/sdk-backend-mockingbird": "11.54.0-alpha.2",
99
+ "@gooddata/sdk-ui-theme-provider": "11.54.0-alpha.2",
100
+ "@gooddata/stylelint-config": "11.54.0-alpha.2"
101
101
  },
102
102
  "peerDependencies": {
103
103
  "react": "^18.0.0 || ^19.0.0",