@gooddata/sdk-ui-filters 11.57.0-alpha.5 → 11.57.0-alpha.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.
Files changed (57) hide show
  1. package/esm/DateFilter/AbsoluteDateFilterForm/AbsoluteDateFilterForm.d.ts +12 -2
  2. package/esm/DateFilter/AbsoluteDateFilterForm/AbsoluteDateFilterForm.d.ts.map +1 -1
  3. package/esm/DateFilter/AbsoluteDateFilterForm/AbsoluteDateFilterForm.js +4 -4
  4. package/esm/DateFilter/AbsoluteDateFilterForm/conversions.d.ts +1 -0
  5. package/esm/DateFilter/AbsoluteDateFilterForm/conversions.d.ts.map +1 -1
  6. package/esm/DateFilter/AbsoluteDateFilterForm/conversions.js +8 -0
  7. package/esm/DateFilter/DateFilterBody/DateFilterBody.d.ts.map +1 -1
  8. package/esm/DateFilter/DateFilterBody/DateFilterBody.js +8 -3
  9. package/esm/DateFilter/DateFilterBody/DateFilterFormContent.d.ts +2 -1
  10. package/esm/DateFilter/DateFilterBody/DateFilterFormContent.d.ts.map +1 -1
  11. package/esm/DateFilter/DateFilterBody/DateFilterFormContent.js +22 -19
  12. package/esm/DateFilter/PeriodRangePicker/AccessibleFieldInput.d.ts +19 -0
  13. package/esm/DateFilter/PeriodRangePicker/AccessibleFieldInput.d.ts.map +1 -0
  14. package/esm/DateFilter/PeriodRangePicker/AccessibleFieldInput.js +91 -0
  15. package/esm/DateFilter/PeriodRangePicker/PeriodRangePickerImpl.d.ts +23 -3
  16. package/esm/DateFilter/PeriodRangePicker/PeriodRangePickerImpl.d.ts.map +1 -1
  17. package/esm/DateFilter/PeriodRangePicker/PeriodRangePickerImpl.js +349 -38
  18. package/esm/DateFilter/PeriodRangePicker/dateFnsRangePicker.d.ts +9 -0
  19. package/esm/DateFilter/PeriodRangePicker/dateFnsRangePicker.d.ts.map +1 -0
  20. package/esm/DateFilter/PeriodRangePicker/dateFnsRangePicker.js +79 -0
  21. package/esm/DateFilter/PeriodRangePicker/periodRangePickerAccessibility.d.ts +57 -0
  22. package/esm/DateFilter/PeriodRangePicker/periodRangePickerAccessibility.d.ts.map +1 -0
  23. package/esm/DateFilter/PeriodRangePicker/periodRangePickerAccessibility.js +47 -0
  24. package/esm/DateFilter/PeriodRangePicker/types.d.ts +12 -1
  25. package/esm/DateFilter/PeriodRangePicker/types.d.ts.map +1 -1
  26. package/esm/DateFilter/PeriodRangePicker/weekStartDateFnsLocale.d.ts +28 -0
  27. package/esm/DateFilter/PeriodRangePicker/weekStartDateFnsLocale.d.ts.map +1 -0
  28. package/esm/DateFilter/PeriodRangePicker/weekStartDateFnsLocale.js +47 -0
  29. package/esm/DateFilter/accessibility/keyboardNavigation.d.ts +6 -7
  30. package/esm/DateFilter/accessibility/keyboardNavigation.d.ts.map +1 -1
  31. package/esm/DateFilter/accessibility/keyboardNavigation.js +3 -2
  32. package/esm/DateFilter/constants/Platform.d.ts +1 -0
  33. package/esm/DateFilter/constants/Platform.d.ts.map +1 -1
  34. package/esm/DateFilter/constants/Platform.js +2 -0
  35. package/esm/DateFilter/utils/StaticPeriodConversions.d.ts +6 -0
  36. package/esm/DateFilter/utils/StaticPeriodConversions.d.ts.map +1 -1
  37. package/esm/DateFilter/utils/StaticPeriodConversions.js +13 -0
  38. package/esm/FilterGroup/FilterGroup.d.ts.map +1 -1
  39. package/esm/FilterGroup/FilterGroup.js +1 -4
  40. package/esm/sdk-ui-filters.d.ts +11 -1
  41. package/esm/shared/utils/domUtilities.d.ts +9 -0
  42. package/esm/shared/utils/domUtilities.d.ts.map +1 -0
  43. package/esm/shared/utils/domUtilities.js +12 -0
  44. package/package.json +15 -15
  45. package/styles/css/components/PeriodRangePicker.css +20 -42
  46. package/styles/css/components/PeriodRangePicker.css.map +1 -1
  47. package/styles/css/dateFilter.css +20 -42
  48. package/styles/css/dateFilter.css.map +1 -1
  49. package/styles/css/main.css +20 -42
  50. package/styles/css/main.css.map +1 -1
  51. package/styles/scss/components/PeriodRangePicker.scss +38 -49
  52. package/esm/DateFilter/PeriodRangePicker/momentRangePicker.d.ts +0 -9
  53. package/esm/DateFilter/PeriodRangePicker/momentRangePicker.d.ts.map +0 -1
  54. package/esm/DateFilter/PeriodRangePicker/momentRangePicker.js +0 -9
  55. package/esm/DateFilter/PeriodRangePicker/weekStartMomentLocale.d.ts +0 -16
  56. package/esm/DateFilter/PeriodRangePicker/weekStartMomentLocale.d.ts.map +0 -1
  57. package/esm/DateFilter/PeriodRangePicker/weekStartMomentLocale.js +0 -30
@@ -1,4 +1,4 @@
1
- import { type ReactNode } from "react";
1
+ import { type ReactNode, type RefObject } from "react";
2
2
  import { type DateFilterGranularity, type WeekStart } from "@gooddata/sdk-model";
3
3
  import { type IAccessibilityConfigBase } from "@gooddata/sdk-ui-kit";
4
4
  import { type DateFilterOption, type IUiAbsoluteDateFilterForm } from "../interfaces/index.js";
@@ -26,6 +26,11 @@ export interface IAbsoluteDateFilterFormProps {
26
26
  withoutApply?: boolean;
27
27
  submitForm: () => void;
28
28
  customRangeHint?: ReactNode;
29
+ /**
30
+ * Reports whether the `PeriodRangePicker` branch's fields currently form a submittable range - see
31
+ * `IPeriodRangePickerProps.onValidityChange`. Not called for the classic `DateRangePicker` branch.
32
+ */
33
+ onPeriodRangeValidityChange?: (isValid: boolean) => void;
29
34
  /**
30
35
  * Enables Day/Week/Month/Quarter/Year granularity switching.
31
36
  *
@@ -43,9 +48,14 @@ export interface IAbsoluteDateFilterFormProps {
43
48
  * Accessibility configuration for the absolute date filter form.
44
49
  */
45
50
  accessibilityConfig?: IAbsoluteDateFilterFormAccessibilityConfig;
51
+ /**
52
+ * Ref for the granularity tabs' wrapping element - used for keyboard navigation focus management.
53
+ * Only relevant while {@link IAbsoluteDateFilterFormProps.isGranularityEnabled} renders the tabs.
54
+ */
55
+ granularityTabsRef?: RefObject<HTMLDivElement | null>;
46
56
  }
47
57
  /**
48
58
  * @internal
49
59
  */
50
- export declare function AbsoluteDateFilterForm({ dateFormat, isMobile, selectedFilterOption, isTimeEnabled, isSecondsEnabled, weekStart, shouldOverlayDatePicker, withoutApply, onSelectedFilterOptionChange, submitForm, customRangeHint, isGranularityEnabled, availableGranularities, accessibilityConfig, }: IAbsoluteDateFilterFormProps): import("react/jsx-runtime").JSX.Element;
60
+ export declare function AbsoluteDateFilterForm({ dateFormat, isMobile, selectedFilterOption, isTimeEnabled, isSecondsEnabled, weekStart, shouldOverlayDatePicker, withoutApply, onSelectedFilterOptionChange, submitForm, customRangeHint, onPeriodRangeValidityChange, isGranularityEnabled, availableGranularities, accessibilityConfig, granularityTabsRef, }: IAbsoluteDateFilterFormProps): import("react/jsx-runtime").JSX.Element;
51
61
  //# sourceMappingURL=AbsoluteDateFilterForm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AbsoluteDateFilterForm.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/AbsoluteDateFilterForm/AbsoluteDateFilterForm.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,OAAO,EAEH,KAAK,qBAAqB,EAC1B,KAAK,SAAS,EACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAMrE,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAU/F;;GAEG;AACH,MAAM,WAAW,0CAA2C,SAAQ,wBAAwB;IACxF;;OAEG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,yBAAyB,CAAC;IAChD,QAAQ,EAAE,OAAO,CAAC;IAClB,4BAA4B,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACjE,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;OAEG;IACH,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;IAChD;;OAEG;IACH,mBAAmB,CAAC,EAAE,0CAA0C,CAAC;CACpE;AAYD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,EACnC,UAAU,EACV,QAAQ,EACR,oBAAoB,EACpB,aAAa,EACb,gBAAwB,EACxB,SAAS,EACT,uBAAuB,EACvB,YAAY,EACZ,4BAA4B,EAC5B,UAAU,EACV,eAAe,EACf,oBAA4B,EAC5B,sBAAsB,EACtB,mBAAmB,GACtB,EAAE,4BAA4B,2CA+F9B"}
1
+ {"version":3,"file":"AbsoluteDateFilterForm.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/AbsoluteDateFilterForm/AbsoluteDateFilterForm.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvD,OAAO,EAEH,KAAK,qBAAqB,EAC1B,KAAK,SAAS,EACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAMrE,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAW/F;;GAEG;AACH,MAAM,WAAW,0CAA2C,SAAQ,wBAAwB;IACxF;;OAEG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,yBAAyB,CAAC;IAChD,QAAQ,EAAE,OAAO,CAAC;IAClB,4BAA4B,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACjE,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B;;;OAGG;IACH,2BAA2B,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACzD;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;OAEG;IACH,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;IAChD;;OAEG;IACH,mBAAmB,CAAC,EAAE,0CAA0C,CAAC;IACjE;;;OAGG;IACH,kBAAkB,CAAC,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;CACzD;AAYD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,EACnC,UAAU,EACV,QAAQ,EACR,oBAAoB,EACpB,aAAa,EACb,gBAAwB,EACxB,SAAS,EACT,uBAAuB,EACvB,YAAY,EACZ,4BAA4B,EAC5B,UAAU,EACV,eAAe,EACf,2BAA2B,EAC3B,oBAA4B,EAC5B,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,GACrB,EAAE,4BAA4B,2CAoG9B"}
@@ -4,7 +4,7 @@ import { DATE_FILTER_ABSOLUTE_GRANULARITY_TAB_ID } from "../accessibility/elemen
4
4
  import { DateRangePicker } from "../DateRangePicker/DateRangePicker.js";
5
5
  import { GranularityTabs } from "../GranularityTabs.js";
6
6
  import { PeriodRangePicker } from "../PeriodRangePicker/PeriodRangePicker.js";
7
- import { dateFilterValueToDateRange, dateRangeToDateFilterValue, periodRangeToDateFilterValue, } from "./conversions.js";
7
+ import { dateFilterValueToDateRange, dateFilterValueToPeriodRange, dateRangeToDateFilterValue, periodRangeToDateFilterValue, } from "./conversions.js";
8
8
  const dayPickerProps = {
9
9
  mode: "range",
10
10
  weekStartsOn: 0, // Sunday, regardless of locale
@@ -13,7 +13,7 @@ const isPeriodPickerGranularity = (granularity) => DEFAULT_ABSOLUTE_DATE_FILTER_
13
13
  /**
14
14
  * @internal
15
15
  */
16
- export function AbsoluteDateFilterForm({ dateFormat, isMobile, selectedFilterOption, isTimeEnabled, isSecondsEnabled = false, weekStart, shouldOverlayDatePicker, withoutApply, onSelectedFilterOptionChange, submitForm, customRangeHint, isGranularityEnabled = false, availableGranularities, accessibilityConfig, }) {
16
+ export function AbsoluteDateFilterForm({ dateFormat, isMobile, selectedFilterOption, isTimeEnabled, isSecondsEnabled = false, weekStart, shouldOverlayDatePicker, withoutApply, onSelectedFilterOptionChange, submitForm, customRangeHint, onPeriodRangeValidityChange, isGranularityEnabled = false, availableGranularities, accessibilityConfig, granularityTabsRef, }) {
17
17
  const selectedGranularity = selectedFilterOption.granularity ?? availableGranularities[0];
18
18
  const showGranularityTabs = isGranularityEnabled && availableGranularities.length > 0;
19
19
  const handleRangeChange = (range) => {
@@ -44,6 +44,6 @@ export function AbsoluteDateFilterForm({ dateFormat, isMobile, selectedFilterOpt
44
44
  // TODO MC-5322: time granularity is not supported in the new picker yet - the hour/minute/second can't be
45
45
  // modified, so the selected range always spans the whole day (00:00-23:59[:59] once time is enabled).
46
46
  // This will be added as part of MC-5322.
47
- const picker = showGranularityTabs && isPeriodPickerGranularity(selectedGranularity) ? (_jsx(PeriodRangePicker, { granularity: selectedGranularity, range: { from: selectedFilterOption.from, to: selectedFilterOption.to }, onRangeChange: handlePeriodRangeChange, isMobile: isMobile, weekStart: weekStart, withoutApply: withoutApply, submitForm: submitForm }, selectedGranularity)) : (_jsx(DateRangePicker, { dateFormat: dateFormat, onRangeChange: handleRangeChange, range: dateFilterValueToDateRange(selectedFilterOption, isTimeEnabled, isSecondsEnabled), isMobile: isMobile, dayPickerProps: dayPickerProps, isTimeEnabled: isTimeEnabled, isSecondsEnabled: isSecondsEnabled, weekStart: weekStart, shouldOverlayDatePicker: shouldOverlayDatePicker, submitForm: submitForm, withoutApply: withoutApply, customRangeHint: customRangeHint }));
48
- return (_jsxs(_Fragment, { children: [showGranularityTabs ? (_jsx(GranularityTabs, { availableGranularities: availableGranularities, selectedGranularity: selectedGranularity, onSelectedGranularityChange: handleGranularityChange, accessibilityConfig: { ariaControls: accessibilityConfig?.id }, className: "gd-absolute-filter-form-granularity-tabs s-absolute-filter-form-granularity-tabs", selectedTabId: DATE_FILTER_ABSOLUTE_GRANULARITY_TAB_ID })) : null, showGranularityTabs ? (_jsx("div", { id: accessibilityConfig?.id, role: "tabpanel", "aria-labelledby": DATE_FILTER_ABSOLUTE_GRANULARITY_TAB_ID, children: picker })) : (picker)] }));
47
+ const picker = showGranularityTabs && isPeriodPickerGranularity(selectedGranularity) ? (_jsx(PeriodRangePicker, { granularity: selectedGranularity, range: dateFilterValueToPeriodRange(selectedFilterOption), onRangeChange: handlePeriodRangeChange, isMobile: isMobile, weekStart: weekStart, withoutApply: withoutApply, submitForm: submitForm, dateFormat: dateFormat, customRangeHint: customRangeHint, onValidityChange: onPeriodRangeValidityChange }, selectedGranularity)) : (_jsx(DateRangePicker, { dateFormat: dateFormat, onRangeChange: handleRangeChange, range: dateFilterValueToDateRange(selectedFilterOption, isTimeEnabled, isSecondsEnabled), isMobile: isMobile, dayPickerProps: dayPickerProps, isTimeEnabled: isTimeEnabled, isSecondsEnabled: isSecondsEnabled, weekStart: weekStart, shouldOverlayDatePicker: shouldOverlayDatePicker, submitForm: submitForm, withoutApply: withoutApply, customRangeHint: customRangeHint }));
48
+ return (_jsxs(_Fragment, { children: [showGranularityTabs ? (_jsx("div", { ref: granularityTabsRef, children: _jsx(GranularityTabs, { availableGranularities: availableGranularities, selectedGranularity: selectedGranularity, onSelectedGranularityChange: handleGranularityChange, accessibilityConfig: { ariaControls: accessibilityConfig?.id }, className: "gd-absolute-filter-form-granularity-tabs s-absolute-filter-form-granularity-tabs", selectedTabId: DATE_FILTER_ABSOLUTE_GRANULARITY_TAB_ID }) })) : null, showGranularityTabs ? (_jsx("div", { id: accessibilityConfig?.id, role: "tabpanel", "aria-labelledby": DATE_FILTER_ABSOLUTE_GRANULARITY_TAB_ID, children: picker })) : (picker)] }));
49
49
  }
@@ -4,6 +4,7 @@ import { type IUiAbsoluteDateFilterForm } from "../interfaces/index.js";
4
4
  import { type IPeriodRange } from "../PeriodRangePicker/types.js";
5
5
  export declare const dateRangeToDateFilterValue: (range: IDateRange, localIdentifier: string, isTimeForAbsoluteRangeEnabled: boolean, emptyValueHandling?: EmptyValues, isSecondsForAbsoluteRangeEnabled?: boolean) => IUiAbsoluteDateFilterForm;
6
6
  export declare const dateFilterValueToDateRange: (value: IUiAbsoluteDateFilterForm, isTimeForAbsoluteRangeEnabled?: boolean, isSecondsForAbsoluteRangeEnabled?: boolean) => IDateRange;
7
+ export declare const dateFilterValueToPeriodRange: (value: IUiAbsoluteDateFilterForm) => IPeriodRange;
7
8
  /**
8
9
  * @internal
9
10
  */
@@ -1 +1 @@
1
- {"version":3,"file":"conversions.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/AbsoluteDateFilterForm/conversions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAWxE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAMlE,eAAO,MAAM,0BAA0B,UAC5B,UAAU,mBACA,MAAM,iCACQ,OAAO,uBACjB,WAAW,iDAEjC,yBAeF,CAAC;AAEF,eAAO,MAAM,0BAA0B,UAC5B,yBAAyB,0FAGjC,UAmBF,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,oCAAoC;IACjD,KAAK,EAAE,YAAY,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,6BAA6B,EAAE,OAAO,CAAC;IACvC,kBAAkB,CAAC,EAAE,WAAW,CAAC;IACjC,gCAAgC,CAAC,EAAE,OAAO,CAAC;CAC9C;AAED;;;GAGG;AACH,eAAO,MAAM,4BAA4B,qHAMtC,oCAAoC,KAAG,yBAmBzC,CAAC"}
1
+ {"version":3,"file":"conversions.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/AbsoluteDateFilterForm/conversions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAWxE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAMlE,eAAO,MAAM,0BAA0B,UAC5B,UAAU,mBACA,MAAM,iCACQ,OAAO,uBACjB,WAAW,iDAEjC,yBAeF,CAAC;AAEF,eAAO,MAAM,0BAA0B,UAC5B,yBAAyB,0FAGjC,UAmBF,CAAC;AAEF,eAAO,MAAM,4BAA4B,UAAW,yBAAyB,KAAG,YAQ/E,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,oCAAoC;IACjD,KAAK,EAAE,YAAY,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,6BAA6B,EAAE,OAAO,CAAC;IACvC,kBAAkB,CAAC,EAAE,WAAW,CAAC;IACjC,gCAAgC,CAAC,EAAE,OAAO,CAAC;CAC9C;AAED;;;GAGG;AACH,eAAO,MAAM,4BAA4B,qHAMtC,oCAAoC,KAAG,yBAmBzC,CAAC"}
@@ -33,6 +33,14 @@ export const dateFilterValueToDateRange = (value, isTimeForAbsoluteRangeEnabled
33
33
  }
34
34
  return { from, to };
35
35
  };
36
+ export const dateFilterValueToPeriodRange = (value) => {
37
+ const from = convertPlatformDateStringToDate(value.from);
38
+ const to = convertPlatformDateStringToDate(value.to);
39
+ return {
40
+ from: convertDateToPlatformDateString(from, platformDateFormat) ?? undefined,
41
+ to: convertDateToPlatformDateString(to, platformDateFormat) ?? undefined,
42
+ };
43
+ };
36
44
  /**
37
45
  * Converts a resolved period-picker range into an absolute date filter value, extending it to the
38
46
  * start/end of day when time-of-day is enabled.
@@ -1 +1 @@
1
- {"version":3,"file":"DateFilterBody.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/DateFilterBody/DateFilterBody.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGH,KAAK,YAAY,EACjB,KAAK,SAAS,EAMjB,MAAM,OAAO,CAAC;AAMf,OAAO,EACH,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,SAAS,EAKjB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAGjC,MAAM,wBAAwB,CAAC;AA4BhC,MAAM,WAAW,oBAAoB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,wBAAwB,CAAC;IACxC,oBAAoB,EAAE,gBAAgB,CAAC;IACvC,4BAA4B,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAEjE,oBAAoB,EAAE,OAAO,CAAC;IAC9B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,6BAA6B,EAAE,OAAO,CAAC;IACvC,4BAA4B,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5D,6BAA6B,EAAE,OAAO,CAAC;IACvC,gCAAgC,CAAC,EAAE,OAAO,CAAC;IAE3C,sCAAsC,EAAE,OAAO,CAAC;IAEhD,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;IAEhD,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,IAAI,CAAC;IAE1B,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,gBAAgB,EAAE,YAAY,CAAC;IAE/B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,oBAAoB,EAAE,MAAM,IAAI,CAAC;IAEjC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,eAAe,CAAC,EAAE,gBAAgB,CAAC;IAEnC;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC;;;;OAIG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC;CAC/B;AAED,eAAO,MAAM,cAAc,iHAoczB,CAAC"}
1
+ {"version":3,"file":"DateFilterBody.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/DateFilterBody/DateFilterBody.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGH,KAAK,YAAY,EACjB,KAAK,SAAS,EAMjB,MAAM,OAAO,CAAC;AAMf,OAAO,EACH,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,SAAS,EAKjB,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAGjC,MAAM,wBAAwB,CAAC;AA4BhC,MAAM,WAAW,oBAAoB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,wBAAwB,CAAC;IACxC,oBAAoB,EAAE,gBAAgB,CAAC;IACvC,4BAA4B,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAEjE,oBAAoB,EAAE,OAAO,CAAC;IAC9B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,6BAA6B,EAAE,OAAO,CAAC;IACvC,4BAA4B,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5D,6BAA6B,EAAE,OAAO,CAAC;IACvC,gCAAgC,CAAC,EAAE,OAAO,CAAC;IAE3C,sCAAsC,EAAE,OAAO,CAAC;IAEhD,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;IAEhD,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,IAAI,CAAC;IAE1B,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,gBAAgB,EAAE,YAAY,CAAC;IAE/B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,oBAAoB,EAAE,MAAM,IAAI,CAAC;IAEjC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,eAAe,CAAC,EAAE,gBAAgB,CAAC;IAEnC;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC;;;;OAIG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC;CAC/B;AAED,eAAO,MAAM,cAAc,iHA0czB,CAAC"}
@@ -6,6 +6,7 @@ import { isEmpty } from "lodash-es";
6
6
  import { useIntl } from "react-intl";
7
7
  import { isAbsoluteDateFilterForm, isAllTimeDateFilterOption, isEmptyValuesDateFilterOption, isRelativeDateFilterForm, } from "@gooddata/sdk-model";
8
8
  import { useIdPrefixed } from "@gooddata/sdk-ui-kit";
9
+ import { isEditableElement } from "../../shared/utils/domUtilities.js";
9
10
  import { CheckboxSection } from "../CheckboxSection/CheckboxSection.js";
10
11
  import { getDateFilterOptionGranularity } from "../utils/OptionUtils.js";
11
12
  import { ensureCompatibleAbsoluteGranularity, ensureCompatibleGranularity, filterStandardGranularities, getDefaultCalendarTab, getFilteredGranularities, getFilteredPresets, getFiscalTabsConfig, } from "../utils/presetFilterUtils.js";
@@ -31,6 +32,10 @@ export const DateFilterBody = forwardRef((props, ref) => {
31
32
  const rootRef = useRef(null);
32
33
  const didResetMobileScroll = useRef(false);
33
34
  const didSyncInitialMobileRoute = useRef(false);
35
+ // Gates Apply independently of `errors`: a garbled/emptied PeriodRangePicker field never reaches
36
+ // `onSelectedFilterOptionChange`, so `errors` alone would stay stale and leave Apply enabled - see
37
+ // `IPeriodRangePickerProps.onValidityChange`.
38
+ const [isPeriodRangeValid, setIsPeriodRangeValid] = useState(true);
34
39
  const intl = useIntl();
35
40
  const fiscalTabsConfig = getFiscalTabsConfig(props.filterOptions.relativePreset, props.activeCalendars);
36
41
  const [selectedTab, setSelectedTab] = useState(() => getDefaultCalendarTab(props.activeCalendars, props.selectedFilterOption));
@@ -164,7 +169,7 @@ export const DateFilterBody = forwardRef((props, ref) => {
164
169
  const hasFormOptions = !!(filterOptions.absoluteForm ||
165
170
  (filterOptions.relativeForm && availableGranularities.length > 0));
166
171
  const handleKeyDown = (e) => {
167
- if (e.key === "ArrowLeft" && route) {
172
+ if (e.key === "ArrowLeft" && route && !isEditableElement(e.target)) {
168
173
  e.preventDefault();
169
174
  e.stopPropagation();
170
175
  handleBackNavigation();
@@ -188,7 +193,7 @@ export const DateFilterBody = forwardRef((props, ref) => {
188
193
  return (_jsxs("div", { ref: rootRef, className: cx(!isMobile && "gd-extended-date-filter-body-redesigned", "gd-extended-date-filter-body", "s-extended-date-filters-body", isMobile && "gd-extended-date-filter-body-mobile", route && "gd-active-form"), onKeyDown: handleKeyDown, onFocus: handleFocus, role: "dialog", id: formId, "aria-label": formLabel, children: [route === null && isMobile ? (_jsx("div", { className: "gd-extended-date-filter-mobile-header", onClick: () => {
189
194
  onCancelClick();
190
195
  closeDropdown();
191
- }, children: dateFilterButton })) : null, route ? (_jsx("div", { className: "gd-extended-date-filter-form-wrapper", ref: ref, children: _jsx(DateFilterFormContent, { filterOptions: filterOptions, selectedFilterOption: selectedFilterOption, onSelectedFilterOptionChange: onSelectedFilterOptionChange, dateFormat: dateFormat, weekStart: weekStart, isTimeForAbsoluteRangeEnabled: isTimeForAbsoluteRangeEnabled, isSecondsForAbsoluteRangeEnabled: isSecondsForAbsoluteRangeEnabled, isAbsoluteDateFilterGranularityEnabled: props.isAbsoluteDateFilterGranularityEnabled, availableGranularities: filteredAvailableGranularities, absoluteAvailableGranularities: filteredAbsoluteAvailableGranularities, isMobile: isMobile, withoutApply: withoutApply, enableEmptyDateValues: props.enableEmptyDateValues, customRangeHint: props.customRangeHint, activeForm: route, onBackNavigation: handleBackNavigation, onClose: closeDropdown, submitForm: () => {
196
+ }, children: dateFilterButton })) : null, route ? (_jsx("div", { className: "gd-extended-date-filter-form-wrapper", ref: ref, children: _jsx(DateFilterFormContent, { filterOptions: filterOptions, selectedFilterOption: selectedFilterOption, onSelectedFilterOptionChange: onSelectedFilterOptionChange, dateFormat: dateFormat, weekStart: weekStart, isTimeForAbsoluteRangeEnabled: isTimeForAbsoluteRangeEnabled, isSecondsForAbsoluteRangeEnabled: isSecondsForAbsoluteRangeEnabled, isAbsoluteDateFilterGranularityEnabled: props.isAbsoluteDateFilterGranularityEnabled, availableGranularities: filteredAvailableGranularities, absoluteAvailableGranularities: filteredAbsoluteAvailableGranularities, isMobile: isMobile, withoutApply: withoutApply, enableEmptyDateValues: props.enableEmptyDateValues, customRangeHint: props.customRangeHint, onPeriodRangeValidityChange: setIsPeriodRangeValid, activeForm: route, onBackNavigation: handleBackNavigation, onClose: closeDropdown, submitForm: () => {
192
197
  onApplyClick();
193
198
  if (!withoutApply) {
194
199
  closeDropdown();
@@ -224,7 +229,7 @@ export const DateFilterBody = forwardRef((props, ref) => {
224
229
  : undefined }), _jsxs("div", { role: "group", className: cx("gd-extended-date-filter-actions"), children: [_jsx("div", { className: "gd-extended-date-filter-actions-left-content", children: isConfigurationEnabled ? (_jsx(DateFilterConfigurationButton, { onConfiguration: onConfigurationClick })) : null }), _jsxs("div", { className: "gd-extended-date-filter-actions-buttons", children: [_jsx(DateFilterBodyButton, { messageId: withoutApply ? "close" : "cancel", className: cx("gd-button-secondary", isMobile ? "gd-button-medium" : "gd-button-small", withoutApply ? "s-date-filter-close" : "s-date-filter-cancel"), onClick: () => {
225
230
  onCancelClick();
226
231
  closeDropdown();
227
- } }), !withoutApply && (_jsx(DateFilterBodyButton, { messageId: "apply", className: cx("gd-button-action", isMobile ? "gd-button-medium" : "gd-button-small", "s-date-filter-apply"), disabled: !isEmpty(errors), onClick: () => {
232
+ } }), !withoutApply && (_jsx(DateFilterBodyButton, { messageId: "apply", className: cx("gd-button-action", isMobile ? "gd-button-medium" : "gd-button-small", "s-date-filter-apply"), disabled: !isEmpty(errors) || !isPeriodRangeValid, onClick: () => {
228
233
  onApplyClick();
229
234
  closeDropdown();
230
235
  }, describedByFromValidation: true }))] })] })] }));
@@ -17,6 +17,7 @@ export interface IDateFilterFormContentProps {
17
17
  withoutApply?: boolean;
18
18
  enableEmptyDateValues?: boolean;
19
19
  customRangeHint?: ReactNode;
20
+ onPeriodRangeValidityChange?: (isValid: boolean) => void;
20
21
  activeForm: DateFilterRoute;
21
22
  onBackNavigation: () => void;
22
23
  onClose: () => void;
@@ -29,5 +30,5 @@ export interface IDateFilterFormContentProps {
29
30
  *
30
31
  * @internal
31
32
  */
32
- export declare function DateFilterFormContent({ filterOptions, selectedFilterOption, onSelectedFilterOptionChange, dateFormat, weekStart, isTimeForAbsoluteRangeEnabled, isSecondsForAbsoluteRangeEnabled, isAbsoluteDateFilterGranularityEnabled, availableGranularities, absoluteAvailableGranularities, isMobile, withoutApply, enableEmptyDateValues, customRangeHint, activeForm, onBackNavigation, onClose, submitForm, errors, }: IDateFilterFormContentProps): import("react/jsx-runtime").JSX.Element;
33
+ export declare function DateFilterFormContent({ filterOptions, selectedFilterOption, onSelectedFilterOptionChange, dateFormat, weekStart, isTimeForAbsoluteRangeEnabled, isSecondsForAbsoluteRangeEnabled, isAbsoluteDateFilterGranularityEnabled, availableGranularities, absoluteAvailableGranularities, isMobile, withoutApply, enableEmptyDateValues, customRangeHint, onPeriodRangeValidityChange, activeForm, onBackNavigation, onClose, submitForm, errors, }: IDateFilterFormContentProps): import("react/jsx-runtime").JSX.Element;
33
34
  //# sourceMappingURL=DateFilterFormContent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DateFilterFormContent.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/DateFilterBody/DateFilterFormContent.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,SAAS,EAAuB,MAAM,OAAO,CAAC;AAKhF,OAAO,EACH,KAAK,qBAAqB,EAC1B,KAAK,SAAS,EAKjB,MAAM,qBAAqB,CAAC;AAU7B,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAGjC,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,2BAA2B;IACxC,aAAa,EAAE,wBAAwB,CAAC;IACxC,oBAAoB,EAAE,gBAAgB,CAAC;IACvC,4BAA4B,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAEjE,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,SAAS,CAAC;IACrB,6BAA6B,EAAE,OAAO,CAAC;IACvC,gCAAgC,CAAC,EAAE,OAAO,CAAC;IAC3C,sCAAsC,EAAE,OAAO,CAAC;IAChD,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;IAChD,8BAA8B,EAAE,qBAAqB,EAAE,CAAC;IAExD,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,eAAe,CAAC,EAAE,SAAS,CAAC;IAE5B,UAAU,EAAE,eAAe,CAAC;IAC5B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,yBAAyB,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,EAClC,aAAa,EACb,oBAAoB,EACpB,4BAA4B,EAC5B,UAAU,EACV,SAAS,EACT,6BAA6B,EAC7B,gCAAgC,EAChC,sCAAsC,EACtC,sBAAsB,EACtB,8BAA8B,EAC9B,QAAQ,EACR,YAAY,EACZ,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,OAAO,EACP,UAAU,EACV,MAAM,GACT,EAAE,2BAA2B,2CA+I7B"}
1
+ {"version":3,"file":"DateFilterFormContent.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/DateFilterBody/DateFilterFormContent.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,SAAS,EAAuC,MAAM,OAAO,CAAC;AAKhG,OAAO,EACH,KAAK,qBAAqB,EAC1B,KAAK,SAAS,EAKjB,MAAM,qBAAqB,CAAC;AAU7B,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAGjC,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,2BAA2B;IACxC,aAAa,EAAE,wBAAwB,CAAC;IACxC,oBAAoB,EAAE,gBAAgB,CAAC;IACvC,4BAA4B,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAEjE,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,SAAS,CAAC;IACrB,6BAA6B,EAAE,OAAO,CAAC;IACvC,gCAAgC,CAAC,EAAE,OAAO,CAAC;IAC3C,sCAAsC,EAAE,OAAO,CAAC;IAChD,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;IAChD,8BAA8B,EAAE,qBAAqB,EAAE,CAAC;IAExD,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B,2BAA2B,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAEzD,UAAU,EAAE,eAAe,CAAC;IAC5B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,yBAAyB,CAAC;CACtC;AAkBD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,EAClC,aAAa,EACb,oBAAoB,EACpB,4BAA4B,EAC5B,UAAU,EACV,SAAS,EACT,6BAA6B,EAC7B,gCAAgC,EAChC,sCAAsC,EACtC,sBAAsB,EACtB,8BAA8B,EAC9B,QAAQ,EACR,YAAY,EACZ,qBAAqB,EACrB,eAAe,EACf,2BAA2B,EAC3B,UAAU,EACV,gBAAgB,EAChB,OAAO,EACP,UAAU,EACV,MAAM,GACT,EAAE,2BAA2B,2CA4I7B"}
@@ -6,18 +6,27 @@ import { useIntl } from "react-intl";
6
6
  import { isAbsoluteDateFilterForm, isAllTimeDateFilterOption, isEmptyValuesDateFilterOption, isRelativeDateFilterForm, } from "@gooddata/sdk-model";
7
7
  import { useId } from "@gooddata/sdk-ui-kit";
8
8
  import { AbsoluteDateFilterForm } from "../AbsoluteDateFilterForm/AbsoluteDateFilterForm.js";
9
- import { createDateFilterRelativeFormKeyboardHandler, submitRelativeDateFilterForm, } from "../accessibility/keyboardNavigation.js";
9
+ import { createGranularityTabsKeyboardHandler, submitRelativeDateFilterForm, } from "../accessibility/keyboardNavigation.js";
10
10
  import { DateFilterFormWrapper } from "../DateFilterFormWrapper/DateFilterFormWrapper.js";
11
11
  import { EmptyValuesHandlingToggle } from "../EmptyValuesHandlingToggle/EmptyValuesHandlingToggle.js";
12
12
  import { RelativeDateFilterForm } from "../RelativeDateFilterForm/RelativeDateFilterForm.js";
13
13
  import { DateFilterFormNavigationWrapper } from "./DateFilterFormNavigationWrapper.js";
14
+ /**
15
+ * Builds the keydown handler bound to a granularity tabs container, shared by the absolute and relative
16
+ * form's wrapper `<div>`s below - the only two consumers, hence a local hook rather than a shared export.
17
+ */
18
+ function useGranularityTabsKeydownHandler(tabGranularityRef, closeDropdown) {
19
+ return useCallback((event) => {
20
+ createGranularityTabsKeyboardHandler({ tabGranularityRef, closeDropdown })(event);
21
+ }, [tabGranularityRef, closeDropdown]);
22
+ }
14
23
  /**
15
24
  * Component that renders the form content (absolute or relative) for date filter.
16
25
  * This component is purely presentational and doesn't manage any navigation state.
17
26
  *
18
27
  * @internal
19
28
  */
20
- export function DateFilterFormContent({ filterOptions, selectedFilterOption, onSelectedFilterOptionChange, dateFormat, weekStart, isTimeForAbsoluteRangeEnabled, isSecondsForAbsoluteRangeEnabled, isAbsoluteDateFilterGranularityEnabled, availableGranularities, absoluteAvailableGranularities, isMobile, withoutApply, enableEmptyDateValues, customRangeHint, activeForm, onBackNavigation, onClose, submitForm, errors, }) {
29
+ export function DateFilterFormContent({ filterOptions, selectedFilterOption, onSelectedFilterOptionChange, dateFormat, weekStart, isTimeForAbsoluteRangeEnabled, isSecondsForAbsoluteRangeEnabled, isAbsoluteDateFilterGranularityEnabled, availableGranularities, absoluteAvailableGranularities, isMobile, withoutApply, enableEmptyDateValues, customRangeHint, onPeriodRangeValidityChange, activeForm, onBackNavigation, onClose, submitForm, errors, }) {
21
30
  const showStaticForm = activeForm === "absoluteForm" && filterOptions.absoluteForm;
22
31
  const showRelativeForm = activeForm === "relativeForm" && filterOptions.relativeForm && availableGranularities.length > 0;
23
32
  const absoluteSelectedFilterOption = filterOptions.absoluteForm && isAbsoluteDateFilterForm(selectedFilterOption)
@@ -27,8 +36,8 @@ export function DateFilterFormContent({ filterOptions, selectedFilterOption, onS
27
36
  ? selectedFilterOption
28
37
  : filterOptions.relativeForm;
29
38
  const intl = useIntl();
30
- const relativeDateFilterRef = useRef(null);
31
39
  const tabGranularityRef = useRef(null);
40
+ const absoluteTabGranularityRef = useRef(null);
32
41
  const relativeDateFilterId = useId();
33
42
  const absoluteDateFilterId = useId();
34
43
  const shouldRenderEmptyValuesHandling = !!enableEmptyDateValues &&
@@ -37,25 +46,19 @@ export function DateFilterFormContent({ filterOptions, selectedFilterOption, onS
37
46
  selectedFilterOption.emptyValueHandling !== "only";
38
47
  const effectiveEmptyValueHandling = selectedFilterOption.emptyValueHandling ?? "exclude";
39
48
  const emptyValueHandlingToggleChecked = effectiveEmptyValueHandling === "include";
40
- const handleRelativeDateFilterKeydown = useCallback((event, closeDropdown) => {
41
- const keyboardHandler = createDateFilterRelativeFormKeyboardHandler({
42
- relativeDateFilterRef,
43
- tabGranularityRef,
44
- closeDropdown,
45
- });
46
- keyboardHandler(event);
47
- }, [relativeDateFilterRef, tabGranularityRef]);
49
+ const handleRelativeDateFilterKeydown = useGranularityTabsKeydownHandler(tabGranularityRef, onClose);
50
+ const handleAbsoluteDateFilterKeydown = useGranularityTabsKeydownHandler(absoluteTabGranularityRef, onClose);
48
51
  return (_jsxs("div", { className: "gd-date-filter-form-content", children: [showStaticForm ? (_jsx(DateFilterFormNavigationWrapper, { title: filterOptions.absoluteForm?.name
49
52
  ? filterOptions.absoluteForm.name
50
- : intl.formatMessage({ id: "filters.staticPeriod" }), onBack: onBackNavigation, backLabel: intl.formatMessage({ id: "menu.back" }), isMobile: isMobile, children: _jsxs(DateFilterFormWrapper, { isMobile: isMobile, children: [_jsx(AbsoluteDateFilterForm, { dateFormat: dateFormat, onSelectedFilterOptionChange: onSelectedFilterOptionChange, selectedFilterOption: absoluteSelectedFilterOption, isMobile: isMobile, isTimeEnabled: isTimeForAbsoluteRangeEnabled, isSecondsEnabled: isSecondsForAbsoluteRangeEnabled, isGranularityEnabled: isAbsoluteDateFilterGranularityEnabled, availableGranularities: absoluteAvailableGranularities, accessibilityConfig: { id: absoluteDateFilterId }, weekStart: weekStart, submitForm: submitForm, withoutApply: withoutApply, customRangeHint: customRangeHint }), shouldRenderEmptyValuesHandling ? (_jsx(EmptyValuesHandlingToggle, { mode: "include", className: "gd-date-filter-empty-values-handling-toggle", checked: emptyValueHandlingToggleChecked, onChange: (checked) => {
51
- const emptyValueHandling = checked ? "include" : undefined;
52
- onSelectedFilterOptionChange({
53
- ...absoluteSelectedFilterOption,
54
- emptyValueHandling,
55
- });
56
- } })) : null] }) })) : null, showRelativeForm ? (_jsx(DateFilterFormNavigationWrapper, { title: filterOptions.relativeForm?.name
53
+ : intl.formatMessage({ id: "filters.staticPeriod" }), onBack: onBackNavigation, backLabel: intl.formatMessage({ id: "menu.back" }), isMobile: isMobile, children: _jsx("div", { onKeyDown: handleAbsoluteDateFilterKeydown, children: _jsxs(DateFilterFormWrapper, { isMobile: isMobile, children: [_jsx(AbsoluteDateFilterForm, { dateFormat: dateFormat, onSelectedFilterOptionChange: onSelectedFilterOptionChange, selectedFilterOption: absoluteSelectedFilterOption, isMobile: isMobile, isTimeEnabled: isTimeForAbsoluteRangeEnabled, isSecondsEnabled: isSecondsForAbsoluteRangeEnabled, isGranularityEnabled: isAbsoluteDateFilterGranularityEnabled, availableGranularities: absoluteAvailableGranularities, accessibilityConfig: { id: absoluteDateFilterId }, weekStart: weekStart, submitForm: submitForm, withoutApply: withoutApply, customRangeHint: customRangeHint, onPeriodRangeValidityChange: onPeriodRangeValidityChange, granularityTabsRef: absoluteTabGranularityRef }), shouldRenderEmptyValuesHandling ? (_jsx(EmptyValuesHandlingToggle, { mode: "include", className: "gd-date-filter-empty-values-handling-toggle", checked: emptyValueHandlingToggleChecked, onChange: (checked) => {
54
+ const emptyValueHandling = checked ? "include" : undefined;
55
+ onSelectedFilterOptionChange({
56
+ ...absoluteSelectedFilterOption,
57
+ emptyValueHandling,
58
+ });
59
+ } })) : null] }) }) })) : null, showRelativeForm ? (_jsx(DateFilterFormNavigationWrapper, { title: filterOptions.relativeForm?.name
57
60
  ? filterOptions.relativeForm.name
58
- : intl.formatMessage({ id: "filters.floatingRange" }), onBack: onBackNavigation, backLabel: intl.formatMessage({ id: "menu.back" }), isMobile: isMobile, children: _jsx("div", { ref: relativeDateFilterRef, onKeyDown: (e) => handleRelativeDateFilterKeydown(e, onClose), children: _jsxs(DateFilterFormWrapper, { isMobile: isMobile, children: [_jsx(RelativeDateFilterForm, { onSelectedFilterOptionChange: onSelectedFilterOptionChange, selectedFilterOption: relativeSelectedFilterOption, availableGranularities: availableGranularities, isMobile: isMobile, ref: tabGranularityRef, relativeDateFilterId: relativeDateFilterId, onKeyDown: (event) => submitRelativeDateFilterForm(event, isEmpty(errors), withoutApply ?? false, onClose, submitForm) }), shouldRenderEmptyValuesHandling ? (_jsx(EmptyValuesHandlingToggle, { mode: "include", className: "gd-date-filter-empty-values-handling-toggle", checked: emptyValueHandlingToggleChecked, onChange: (checked) => {
61
+ : intl.formatMessage({ id: "filters.floatingRange" }), onBack: onBackNavigation, backLabel: intl.formatMessage({ id: "menu.back" }), isMobile: isMobile, children: _jsx("div", { onKeyDown: handleRelativeDateFilterKeydown, children: _jsxs(DateFilterFormWrapper, { isMobile: isMobile, children: [_jsx(RelativeDateFilterForm, { onSelectedFilterOptionChange: onSelectedFilterOptionChange, selectedFilterOption: relativeSelectedFilterOption, availableGranularities: availableGranularities, isMobile: isMobile, ref: tabGranularityRef, relativeDateFilterId: relativeDateFilterId, onKeyDown: (event) => submitRelativeDateFilterForm(event, isEmpty(errors), withoutApply ?? false, onClose, submitForm) }), shouldRenderEmptyValuesHandling ? (_jsx(EmptyValuesHandlingToggle, { mode: "include", className: "gd-date-filter-empty-values-handling-toggle", checked: emptyValueHandlingToggleChecked, onChange: (checked) => {
59
62
  const emptyValueHandling = checked ? "include" : undefined;
60
63
  onSelectedFilterOptionChange({
61
64
  ...relativeSelectedFilterOption,
@@ -0,0 +1,19 @@
1
+ import { type InputHTMLAttributes } from "react";
2
+ interface IAccessibleFieldInputProps extends InputHTMLAttributes<HTMLInputElement> {
3
+ "date-range"?: "start" | "end";
4
+ }
5
+ /**
6
+ * A drop-in replacement for the plain `<input>` rc-picker renders for each half of the date range,
7
+ * used only to layer on the accessibility attributes our a11y model needs - a label, an invalid
8
+ * state, and a description pointing at either a format hint or an error message. Typing, masking,
9
+ * and focus behavior all still belong to rc-picker; this component just decorates the props it's handed.
10
+ *
11
+ * @remarks
12
+ * rc-picker exposes two facts only this component can observe directly: whether the typed text failed to
13
+ * parse (as this input's own invalid state) and whether the field is currently blank (its own live displayed
14
+ * text, which updates on every keystroke including a clear). Both are reported upward, raw and unresolved, via
15
+ * an effect - the surrounding picker owns everything else needed to turn them into an error kind or message.
16
+ */
17
+ export declare const AccessibleFieldInput: import("react").ForwardRefExoticComponent<IAccessibleFieldInputProps & import("react").RefAttributes<HTMLInputElement>>;
18
+ export {};
19
+ //# sourceMappingURL=AccessibleFieldInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccessibleFieldInput.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/PeriodRangePicker/AccessibleFieldInput.tsx"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,mBAAmB,EAM3B,MAAM,OAAO,CAAC;AAMf,UAAU,0BAA2B,SAAQ,mBAAmB,CAAC,gBAAgB,CAAC;IAC9E,YAAY,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;CAClC;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,yHAgG/B,CAAC"}
@@ -0,0 +1,91 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { forwardRef, useCallback, useContext, useEffect, } from "react";
4
+ import { isArrowDownKey, isArrowKey } from "@gooddata/sdk-ui-kit";
5
+ import { PeriodRangeAccessibilityContext } from "./periodRangePickerAccessibility.js";
6
+ /**
7
+ * A drop-in replacement for the plain `<input>` rc-picker renders for each half of the date range,
8
+ * used only to layer on the accessibility attributes our a11y model needs - a label, an invalid
9
+ * state, and a description pointing at either a format hint or an error message. Typing, masking,
10
+ * and focus behavior all still belong to rc-picker; this component just decorates the props it's handed.
11
+ *
12
+ * @remarks
13
+ * rc-picker exposes two facts only this component can observe directly: whether the typed text failed to
14
+ * parse (as this input's own invalid state) and whether the field is currently blank (its own live displayed
15
+ * text, which updates on every keystroke including a clear). Both are reported upward, raw and unresolved, via
16
+ * an effect - the surrounding picker owns everything else needed to turn them into an error kind or message.
17
+ */
18
+ export const AccessibleFieldInput = forwardRef((props, ref) => {
19
+ const { "date-range": side, "aria-invalid": rcPickerInvalid, onKeyDown, ...restProps } = props;
20
+ const accessibility = useContext(PeriodRangeAccessibilityContext);
21
+ const field = side ? accessibility?.[side] : undefined;
22
+ const isCalendarOpen = accessibility?.isCalendarOpen ?? false;
23
+ const onRequestOpen = accessibility?.onRequestOpen;
24
+ const enterCommitRef = accessibility?.enterCommitRef;
25
+ const onEnterCommit = accessibility?.onEnterCommit;
26
+ const ignoreEnter = accessibility?.ignoreEnter ?? false;
27
+ const hasParseError = rcPickerInvalid === true;
28
+ const isBlank = !props.value;
29
+ useEffect(() => {
30
+ if (side) {
31
+ accessibility?.onFieldStateChange(side, { hasParseError, isBlank });
32
+ }
33
+ }, [accessibility, side, hasParseError, isBlank]);
34
+ // rc-picker's own Enter/Escape field-confirmation logic (forwarded as onKeyDown) must still run first;
35
+ // stopping propagation afterwards for arrow/Home/End keys - without preventDefault - lets the browser move
36
+ // the caret normally instead of DateFilterBody/DateFilterCore treating it as back/roving-focus navigation.
37
+ const handleKeyDown = useCallback((e) => {
38
+ // rc-picker confirms the field inside the handler forwarded below (its `onSharedKeyDown` calls
39
+ // `onSubmit()`), but both the resulting `onChange` (when the round completes) and its own
40
+ // deactivation `onBlur` (rc-picker's `useFocusEvents`, which defers via a microtask so that
41
+ // tabbing between this picker's own two fields isn't briefly seen as blurring the whole thing)
42
+ // land one microtask later, not synchronously within this call. The flag has to outlive that
43
+ // microtask too, so the reset below is deferred with `queueMicrotask` rather than done
44
+ // immediately - an immediate reset would already have flipped back to false by the time either
45
+ // callback runs, defeating the guards in handleChange/handleFieldBlur. `finally` still matters
46
+ // for scheduling the reset even if `onKeyDown`/`onEnterCommit` throws.
47
+ const isEnter = e.key === "Enter";
48
+ if (isEnter && ignoreEnter) {
49
+ // In ALL_AT_ONCE mode the caret has to stay where the user is typing, and
50
+ // blur is the only commit point.
51
+ return;
52
+ }
53
+ if (isEnter && enterCommitRef) {
54
+ enterCommitRef.current = true;
55
+ }
56
+ try {
57
+ onKeyDown?.(e);
58
+ if (isEnter) {
59
+ onEnterCommit?.();
60
+ }
61
+ }
62
+ finally {
63
+ if (isEnter && enterCommitRef) {
64
+ queueMicrotask(() => {
65
+ enterCommitRef.current = false;
66
+ });
67
+ }
68
+ }
69
+ if (isArrowKey(e) || e.key === "Home" || e.key === "End") {
70
+ e.stopPropagation();
71
+ }
72
+ // rc-picker already closes its own calendar on Escape internally; without this, the event keeps
73
+ // bubbling up to DateFilterFormContent's outer keydown handler, whose `makeLinearKeyboardNavigation`
74
+ // maps only Escape to closing the whole dropdown. The Tab branch below is not mirroring that
75
+ // mapping - it's a deliberate extra guard so Tab-ing between the two fields, which the browser
76
+ // handles natively, never also reaches that outer handler's own key routing.
77
+ if (isCalendarOpen && (e.key === "Escape" || e.key === "Tab")) {
78
+ e.stopPropagation();
79
+ }
80
+ // rc-picker silently swallows every "open" request while the field is being typed into, so opening
81
+ // the calendar via keyboard has to be detected explicitly here rather than relying on rc-picker's
82
+ // own onOpenChange. Alt+ArrowDown rather than plain ArrowDown: it matches the ARIA combobox pattern's
83
+ // "open the popup without moving focus" binding, and leaves bare ArrowUp/ArrowDown free to move
84
+ // the caret inside the field, which is what the stopPropagation above is there to preserve.
85
+ if (!isCalendarOpen && e.altKey && isArrowDownKey(e)) {
86
+ onRequestOpen?.();
87
+ }
88
+ }, [onKeyDown, isCalendarOpen, onRequestOpen, enterCommitRef, onEnterCommit, ignoreEnter]);
89
+ return (_jsx("input", { ref: ref, ...restProps, "date-range": side, onKeyDown: handleKeyDown, "aria-label": field?.ariaLabel, "aria-invalid": field ? hasParseError || field.errorKind !== undefined : rcPickerInvalid, "aria-describedby": field ? (field.errorKind === undefined ? field.hintId : field.errorId) : undefined }));
90
+ });
91
+ AccessibleFieldInput.displayName = "AccessibleFieldInput";
@@ -1,5 +1,20 @@
1
- import { type Moment } from "moment";
1
+ import { type PeriodRangeSide } from "./periodRangePickerAccessibility.js";
2
2
  import { type IPeriodRange, type IPeriodRangePickerProps, type PeriodRangePickerGranularity } from "./types.js";
3
+ export declare const DATE_FNS_PICKER_FORMATS: {
4
+ readonly fieldDateFormat: "yyyy-MM-dd";
5
+ readonly fieldWeekFormat: "Y-ww";
6
+ readonly fieldMonthFormat: "yyyy-MM";
7
+ readonly fieldQuarterFormat: "yyyy-QQQ";
8
+ readonly fieldYearFormat: "yyyy";
9
+ readonly fieldDateTimeFormat: "yyyy-MM-dd HH:mm";
10
+ readonly fieldTimeFormat: "HH:mm";
11
+ readonly yearFormat: "yyyy";
12
+ readonly cellYearFormat: "yyyy";
13
+ readonly cellQuarterFormat: "QQQ";
14
+ readonly cellDateFormat: "d";
15
+ readonly dayFormat: "d";
16
+ readonly cellMeridiemFormat: "a";
17
+ };
3
18
  /**
4
19
  * Resolves the picker's raw selected [start, end] into the exact day-level range this component reports.
5
20
  *
@@ -9,7 +24,12 @@ import { type IPeriodRange, type IPeriodRangePickerProps, type PeriodRangePicker
9
24
  * within the period" for either bound, so a start-of-unit anchor works as input either way. Day needs no expansion,
10
25
  * and `resolvePeriodBoundaries` handles it as a no-op period (start/end of day), so it's routed through uniformly.
11
26
  */
12
- export declare function resolveSelectedRange(granularity: PeriodRangePickerGranularity, start: Moment, end: Moment, weekStart: NonNullable<IPeriodRangePickerProps["weekStart"]>): IPeriodRange;
27
+ export declare function resolveSelectedRange(granularity: PeriodRangePickerGranularity, start: Date, end: Date, weekStart: NonNullable<IPeriodRangePickerProps["weekStart"]>): IPeriodRange;
28
+ /**
29
+ * Single-sided counterpart to {@link resolveSelectedRange}, used by the blur handler which commits
30
+ * one field at a time.
31
+ */
32
+ export declare function resolveSelectedBoundary(granularity: PeriodRangePickerGranularity, date: Date, side: PeriodRangeSide, weekStart: NonNullable<IPeriodRangePickerProps["weekStart"]>): string;
13
33
  /**
14
34
  * A grid picker for selecting a Week/Month/Quarter/Year period range. Renders the rc-picker panel matching
15
35
  * {@link IPeriodRangePickerProps.granularity} and reports the resolved day-level range via `onRangeChange`.
@@ -18,5 +38,5 @@ export declare function resolveSelectedRange(granularity: PeriodRangePickerGranu
18
38
  * Loaded lazily by `PeriodRangePicker`, which is the public entry point - this module has to stay out of the
19
39
  * package's eager module graph, see the note in PeriodRangePicker.tsx. Import it directly only from tests.
20
40
  */
21
- export declare function PeriodRangePickerImpl({ granularity, range, onRangeChange, isMobile, weekStart, withoutApply, submitForm, }: IPeriodRangePickerProps): import("react/jsx-runtime").JSX.Element;
41
+ export declare function PeriodRangePickerImpl({ granularity, range, onRangeChange, isMobile, weekStart, withoutApply, submitForm, dateFormat, customRangeHint, onValidityChange, }: IPeriodRangePickerProps): import("react/jsx-runtime").JSX.Element;
22
42
  //# sourceMappingURL=PeriodRangePickerImpl.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PeriodRangePickerImpl.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/PeriodRangePicker/PeriodRangePickerImpl.tsx"],"names":[],"mappings":"AA4BA,OAAe,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAU7C,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACpC,MAAM,YAAY,CAAC;AAsEpB;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAChC,WAAW,EAAE,4BAA4B,EACzC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,WAAW,CAAC,uBAAuB,CAAC,WAAW,CAAC,CAAC,GAC7D,YAAY,CAOd;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,EAClC,WAAW,EACX,KAAK,EACL,aAAa,EACb,QAAQ,EACR,SAAoB,EACpB,YAAoB,EACpB,UAAU,GACb,EAAE,uBAAuB,2CAiEzB"}
1
+ {"version":3,"file":"PeriodRangePickerImpl.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/PeriodRangePicker/PeriodRangePickerImpl.tsx"],"names":[],"mappings":"AAgDA,OAAO,EAKH,KAAK,eAAe,EAGvB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACpC,MAAM,YAAY,CAAC;AAgBpB,eAAO,MAAM,uBAAuB;aAChC,eAAe,EAAE,YAAY;aAI7B,eAAe,EAAE,MAAM;aACvB,gBAAgB,EAAE,SAAS;aAC3B,kBAAkB,EAAE,UAAU;aAC9B,eAAe,EAAE,MAAM;aAEvB,mBAAmB,EAAE,kBAAkB;aACvC,eAAe,EAAE,OAAO;aACxB,UAAU,EAAE,MAAM;aAClB,cAAc,EAAE,MAAM;aACtB,iBAAiB,EAAE,KAAK;aACxB,cAAc,EAAE,GAAG;aACnB,SAAS,EAAE,GAAG;aACd,kBAAkB,EAAE,GAAG;CACjB,CAAC;AAkHX;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAChC,WAAW,EAAE,4BAA4B,EACzC,KAAK,EAAE,IAAI,EACX,GAAG,EAAE,IAAI,EACT,SAAS,EAAE,WAAW,CAAC,uBAAuB,CAAC,WAAW,CAAC,CAAC,GAC7D,YAAY,CAOd;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACnC,WAAW,EAAE,4BAA4B,EACzC,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,eAAe,EACrB,SAAS,EAAE,WAAW,CAAC,uBAAuB,CAAC,WAAW,CAAC,CAAC,GAC7D,MAAM,CAER;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,EAClC,WAAW,EACX,KAAK,EACL,aAAa,EACb,QAAQ,EACR,SAAoB,EACpB,YAAoB,EACpB,UAAU,EACV,UAAU,EACV,eAAe,EACf,gBAAgB,GACnB,EAAE,uBAAuB,2CAiXzB"}