@gooddata/sdk-ui-filters 11.58.0-alpha.0 → 11.58.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.
- package/esm/DateFilter/DateFilterButtonLocalized/DateFilterButtonLocalized.d.ts +3 -1
- package/esm/DateFilter/DateFilterButtonLocalized/DateFilterButtonLocalized.d.ts.map +1 -1
- package/esm/DateFilter/DateFilterButtonLocalized/DateFilterButtonLocalized.js +8 -3
- package/esm/DateFilter/DateFilterCore.d.ts.map +1 -1
- package/esm/DateFilter/DateFilterCore.js +1 -1
- package/esm/DateFilter/DateFilterTextLocalized/DateFilterTextLocalized.d.ts +9 -2
- package/esm/DateFilter/DateFilterTextLocalized/DateFilterTextLocalized.d.ts.map +1 -1
- package/esm/DateFilter/DateFilterTextLocalized/DateFilterTextLocalized.js +4 -4
- package/esm/DateFilter/PeriodRangePicker/AccessibleFieldInput.d.ts +11 -8
- package/esm/DateFilter/PeriodRangePicker/AccessibleFieldInput.d.ts.map +1 -1
- package/esm/DateFilter/PeriodRangePicker/AccessibleFieldInput.js +13 -10
- package/esm/DateFilter/PeriodRangePicker/PeriodRangePickerImpl.d.ts +4 -3
- package/esm/DateFilter/PeriodRangePicker/PeriodRangePickerImpl.d.ts.map +1 -1
- package/esm/DateFilter/PeriodRangePicker/PeriodRangePickerImpl.js +87 -25
- package/esm/DateFilter/PeriodRangePicker/PreviewAnnouncer.d.ts +22 -0
- package/esm/DateFilter/PeriodRangePicker/PreviewAnnouncer.d.ts.map +1 -0
- package/esm/DateFilter/PeriodRangePicker/PreviewAnnouncer.js +40 -0
- package/esm/DateFilter/PeriodRangePicker/dateFnsRangePicker.js +1 -1
- package/esm/DateFilter/PeriodRangePicker/types.d.ts +4 -1
- package/esm/DateFilter/PeriodRangePicker/types.d.ts.map +1 -1
- package/esm/DateFilter/index.d.ts +2 -1
- package/esm/DateFilter/index.d.ts.map +1 -1
- package/esm/DateFilter/index.js +2 -1
- package/esm/DateFilter/utils/FormattingUtils.d.ts +4 -0
- package/esm/DateFilter/utils/FormattingUtils.d.ts.map +1 -1
- package/esm/DateFilter/utils/FormattingUtils.js +54 -0
- package/esm/DateFilter/utils/Translations/DateFilterTitle.d.ts +3 -8
- package/esm/DateFilter/utils/Translations/DateFilterTitle.d.ts.map +1 -1
- package/esm/DateFilter/utils/Translations/DateFilterTitle.js +42 -69
- package/esm/DateFilter/{PeriodRangePicker → utils}/weekStartDateFnsLocale.d.ts +1 -2
- package/esm/DateFilter/utils/weekStartDateFnsLocale.d.ts.map +1 -0
- package/esm/DateFilter/{PeriodRangePicker → utils}/weekStartDateFnsLocale.js +3 -4
- package/esm/index.d.ts +2 -2
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +2 -2
- package/esm/sdk-ui-filters.d.ts +6 -3
- package/package.json +13 -13
- package/styles/css/components/PeriodRangePicker.css +18 -8
- package/styles/css/components/PeriodRangePicker.css.map +1 -1
- package/styles/css/dateFilter.css +18 -8
- package/styles/css/dateFilter.css.map +1 -1
- package/styles/css/main.css +18 -8
- package/styles/css/main.css.map +1 -1
- package/styles/scss/components/PeriodRangePicker.scss +37 -12
- package/esm/DateFilter/PeriodRangePicker/weekStartDateFnsLocale.d.ts.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ComponentType, type MutableRefObject } from "react";
|
|
2
|
+
import { type WeekStart } from "@gooddata/sdk-model";
|
|
2
3
|
import { type IFilterButtonCustomIcon } from "../../shared/interfaces/index.js";
|
|
3
4
|
import { type IDateFilterButtonProps } from "../DateFilterButton/DateFilterButton.js";
|
|
4
5
|
import { type DateFilterOption } from "../interfaces/index.js";
|
|
@@ -14,6 +15,7 @@ export interface IDateFilterButtonLocalizedProps {
|
|
|
14
15
|
dropdownId?: string;
|
|
15
16
|
onClick?: () => void;
|
|
16
17
|
ButtonComponent?: ComponentType<IDateFilterButtonProps>;
|
|
18
|
+
weekStart?: WeekStart;
|
|
17
19
|
}
|
|
18
|
-
export declare function DateFilterButtonLocalized({ dateFilterOption, dateFormat, isOpen, isMobile, customFilterName, disabled, customIcon, buttonRef, dropdownId, onClick, ButtonComponent, }: IDateFilterButtonLocalizedProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export declare function DateFilterButtonLocalized({ dateFilterOption, dateFormat, isOpen, isMobile, customFilterName, disabled, customIcon, buttonRef, dropdownId, onClick, ButtonComponent, weekStart, }: IDateFilterButtonLocalizedProps): import("react/jsx-runtime").JSX.Element;
|
|
19
21
|
//# sourceMappingURL=DateFilterButtonLocalized.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DateFilterButtonLocalized.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/DateFilterButtonLocalized/DateFilterButtonLocalized.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,gBAAgB,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"DateFilterButtonLocalized.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/DateFilterButtonLocalized/DateFilterButtonLocalized.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAIlE,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,EAAoB,KAAK,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AAKxG,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAU/D,MAAM,WAAW,+BAA+B;IAC5C,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,uBAAuB,CAAC;IACrC,SAAS,CAAC,EAAE,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,eAAe,CAAC,EAAE,aAAa,CAAC,sBAAsB,CAAC,CAAC;IACxD,SAAS,CAAC,EAAE,SAAS,CAAC;CACzB;AAED,wBAAgB,yBAAyB,CAAC,EACtC,gBAAgB,EAChB,UAAU,EACV,MAAc,EACd,QAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,UAAU,EACV,SAAS,EACT,UAAU,EACV,OAAO,EACP,eAAe,EACf,SAAS,GACZ,EAAE,+BAA+B,2CA4CjC"}
|
|
@@ -9,12 +9,17 @@ const ALIGN_POINT = [
|
|
|
9
9
|
{ align: "bl tr", offset: { x: -2, y: -8 } },
|
|
10
10
|
];
|
|
11
11
|
const TOOLTIP_ALIGN_POINT = [{ align: "bc tc", offset: { x: 0, y: -2 } }];
|
|
12
|
-
export function DateFilterButtonLocalized({ dateFilterOption, dateFormat, isOpen = false, isMobile = true, customFilterName, disabled, customIcon, buttonRef, dropdownId, onClick, ButtonComponent, }) {
|
|
12
|
+
export function DateFilterButtonLocalized({ dateFilterOption, dateFormat, isOpen = false, isMobile = true, customFilterName, disabled, customIcon, buttonRef, dropdownId, onClick, ButtonComponent, weekStart, }) {
|
|
13
13
|
const intl = useIntl();
|
|
14
14
|
const defaultTitle = intl.formatMessage({ id: "dateFilterDropdown.title" });
|
|
15
15
|
const textTitle = customFilterName ?? defaultTitle;
|
|
16
16
|
const title = customFilterName ? (_jsx(ShortenedText, { tooltipAlignPoints: ALIGN_POINT, children: customFilterName })) : (_jsx(_Fragment, { children: textTitle }));
|
|
17
|
-
const textSubtitle = useDateFilterText({
|
|
17
|
+
const textSubtitle = useDateFilterText({
|
|
18
|
+
filter: dateFilterOption,
|
|
19
|
+
dateFormat,
|
|
20
|
+
labelMode: "full",
|
|
21
|
+
weekStart,
|
|
22
|
+
});
|
|
18
23
|
const Component = ButtonComponent ?? DateFilterButton;
|
|
19
|
-
return (_jsx(Component, { title: title, isOpen: isOpen, isMobile: isMobile, disabled: disabled, customIcon: customIcon, customFilterName: customFilterName, onClick: onClick, textTitle: textTitle, textSubtitle: textSubtitle, buttonRef: buttonRef, dropdownId: dropdownId, children: _jsx("span", { className: "s-button-text", children: _jsx(DateFilterTextLocalized, { filter: dateFilterOption, dateFormat: dateFormat, labelMode: "full", shortened: true, tooltipAlignPoints: TOOLTIP_ALIGN_POINT }) }) }));
|
|
24
|
+
return (_jsx(Component, { title: title, isOpen: isOpen, isMobile: isMobile, disabled: disabled, customIcon: customIcon, customFilterName: customFilterName, onClick: onClick, textTitle: textTitle, textSubtitle: textSubtitle, buttonRef: buttonRef, dropdownId: dropdownId, children: _jsx("span", { className: "s-button-text", children: _jsx(DateFilterTextLocalized, { filter: dateFilterOption, dateFormat: dateFormat, labelMode: "full", shortened: true, tooltipAlignPoints: TOOLTIP_ALIGN_POINT, weekStart: weekStart }) }) }));
|
|
20
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DateFilterCore.d.ts","sourceRoot":"","sources":["../../src/DateFilter/DateFilterCore.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"DateFilterCore.d.ts","sourceRoot":"","sources":["../../src/DateFilter/DateFilterCore.tsx"],"names":[],"mappings":"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"}
|
|
@@ -90,7 +90,7 @@ export function DateFilterCore({ originalSelectedFilterOption, originalExcludeCu
|
|
|
90
90
|
? lastValidSelectedFilterOption
|
|
91
91
|
: originalSelectedFilterOption, withoutApply
|
|
92
92
|
? (lastValidExcludeCurrentPeriod ?? false)
|
|
93
|
-
: originalExcludeCurrentPeriod), dateFormat: adjustDateFormatForDisplay(verifiedDateFormat, isTimeForAbsoluteRangeEnabled, isSecondsForAbsoluteRangeEnabled), customFilterName: customFilterName, customIcon: customIcon, onClick: toggleDropdown, ButtonComponent: ButtonComponent, buttonRef: buttonRef ?? undefined, dropdownId: dropdownId }));
|
|
93
|
+
: originalExcludeCurrentPeriod), dateFormat: adjustDateFormatForDisplay(verifiedDateFormat, isTimeForAbsoluteRangeEnabled, isSecondsForAbsoluteRangeEnabled), weekStart: weekStart, customFilterName: customFilterName, customIcon: customIcon, onClick: toggleDropdown, ButtonComponent: ButtonComponent, buttonRef: buttonRef ?? undefined, dropdownId: dropdownId }));
|
|
94
94
|
return (_jsx(Dropdown, { openOnInit: openOnInit, closeOnParentScroll: true, closeOnMouseDrag: true, closeOnOutsideClick: true, enableEventPropagation: true, autofocusOnOpen: true, initialFocus: initialFocus, alignPoints: [
|
|
95
95
|
{ align: "bl tl" },
|
|
96
96
|
{ align: "tr tl" },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type WeekStart } from "@gooddata/sdk-model";
|
|
1
2
|
import { type IAlignPoint } from "@gooddata/sdk-ui-kit";
|
|
2
3
|
import { type DateFilterLabelMode } from "../constants/i18n.js";
|
|
3
4
|
import { type DateFilterOption } from "../interfaces/index.js";
|
|
@@ -7,9 +8,10 @@ interface IDateFilterTextLocalizedProps {
|
|
|
7
8
|
labelMode?: DateFilterLabelMode;
|
|
8
9
|
shortened?: boolean;
|
|
9
10
|
tooltipAlignPoints?: IAlignPoint[];
|
|
11
|
+
weekStart?: WeekStart;
|
|
10
12
|
}
|
|
11
|
-
export declare function DateFilterTextLocalized({ dateFormat, filter, labelMode, shortened, tooltipAlignPoints, }: IDateFilterTextLocalizedProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
-
export declare const useDateFilterText: ({ filter, dateFormat, labelMode, }: {
|
|
13
|
+
export declare function DateFilterTextLocalized({ dateFormat, filter, labelMode, shortened, tooltipAlignPoints, weekStart, }: IDateFilterTextLocalizedProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare const useDateFilterText: ({ filter, dateFormat, labelMode, weekStart, }: {
|
|
13
15
|
filter?: DateFilterOption;
|
|
14
16
|
dateFormat: string;
|
|
15
17
|
/**
|
|
@@ -18,6 +20,11 @@ export declare const useDateFilterText: ({ filter, dateFormat, labelMode, }: {
|
|
|
18
20
|
* - "full": Complete labels suitable for standalone display (buttons, selected values)
|
|
19
21
|
*/
|
|
20
22
|
labelMode?: DateFilterLabelMode;
|
|
23
|
+
/**
|
|
24
|
+
* Which day a week starts on; decides the week number shown for a week-granularity absolute range.
|
|
25
|
+
* Defaults to "Sunday".
|
|
26
|
+
*/
|
|
27
|
+
weekStart?: WeekStart;
|
|
21
28
|
}) => string;
|
|
22
29
|
export {};
|
|
23
30
|
//# sourceMappingURL=DateFilterTextLocalized.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DateFilterTextLocalized.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/DateFilterTextLocalized/DateFilterTextLocalized.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAAiB,MAAM,sBAAsB,CAAC;AAEvE,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,UAAU,6BAA6B;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,kBAAkB,CAAC,EAAE,WAAW,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"DateFilterTextLocalized.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/DateFilterTextLocalized/DateFilterTextLocalized.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,KAAK,WAAW,EAAiB,MAAM,sBAAsB,CAAC;AAEvE,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,UAAU,6BAA6B;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,kBAAkB,CAAC,EAAE,WAAW,EAAE,CAAC;IACnC,SAAS,CAAC,EAAE,SAAS,CAAC;CACzB;AAED,wBAAgB,uBAAuB,CAAC,EACpC,UAAU,EACV,MAAM,EACN,SAAmB,EACnB,SAAiB,EACjB,kBAAkB,EAClB,SAAS,GACZ,EAAE,6BAA6B,2CAM/B;AAED,eAAO,MAAM,iBAAiB,kDAK3B;IACC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;CACzB,WAGA,CAAC"}
|
|
@@ -3,14 +3,14 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { useIntl } from "react-intl";
|
|
4
4
|
import { ShortenedText } from "@gooddata/sdk-ui-kit";
|
|
5
5
|
import { getDateFilterTitleUsingTranslator } from "../utils/Translations/DateFilterTitle.js";
|
|
6
|
-
export function DateFilterTextLocalized({ dateFormat, filter, labelMode = "short", shortened = false, tooltipAlignPoints, }) {
|
|
7
|
-
const dateFilterText = useDateFilterText({ filter, dateFormat, labelMode });
|
|
6
|
+
export function DateFilterTextLocalized({ dateFormat, filter, labelMode = "short", shortened = false, tooltipAlignPoints, weekStart, }) {
|
|
7
|
+
const dateFilterText = useDateFilterText({ filter, dateFormat, labelMode, weekStart });
|
|
8
8
|
if (shortened) {
|
|
9
9
|
return _jsx(ShortenedText, { tooltipAlignPoints: tooltipAlignPoints, children: dateFilterText });
|
|
10
10
|
}
|
|
11
11
|
return _jsx(_Fragment, { children: dateFilterText });
|
|
12
12
|
}
|
|
13
|
-
export const useDateFilterText = ({ filter, dateFormat, labelMode = "short", }) => {
|
|
13
|
+
export const useDateFilterText = ({ filter, dateFormat, labelMode = "short", weekStart, }) => {
|
|
14
14
|
const intl = useIntl();
|
|
15
|
-
return filter ? getDateFilterTitleUsingTranslator(filter, intl, labelMode, dateFormat) : "";
|
|
15
|
+
return filter ? getDateFilterTitleUsingTranslator(filter, intl, labelMode, dateFormat, weekStart) : "";
|
|
16
16
|
};
|
|
@@ -3,16 +3,19 @@ interface IAccessibleFieldInputProps extends InputHTMLAttributes<HTMLInputElemen
|
|
|
3
3
|
"date-range"?: "start" | "end";
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* and focus behavior all still belong to rc-picker; this component just decorates the props it's handed.
|
|
6
|
+
* Replaces the plain `<input>` rc-picker renders for each half of the date range, adding the accessibility
|
|
7
|
+
* attributes our a11y model needs - a label, an invalid state, and a description pointing at a format hint
|
|
8
|
+
* or an error - plus that field's own leading calendar icon. Typing, masking and focus stay with rc-picker.
|
|
10
9
|
*
|
|
11
10
|
* @remarks
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* The icon is rendered here because rc-picker's own icon props render once for the whole range control,
|
|
12
|
+
* not once per field. Returning a fragment is safe: rc-picker reaches the input through `ref`.
|
|
13
|
+
* The icon is inert - `aria-hidden` plus `pointer-events: none` - so it adds no tab stop, and it is
|
|
14
|
+
* positioned over the input rather than beside it, so a click on it activates this field.
|
|
15
|
+
*
|
|
16
|
+
* Two facts are only observable here: whether the typed text failed to parse, and whether the field is
|
|
17
|
+
* currently blank. Both are reported upward raw, via an effect; the surrounding picker turns them into an
|
|
18
|
+
* error kind or message.
|
|
16
19
|
*/
|
|
17
20
|
export declare const AccessibleFieldInput: import("react").ForwardRefExoticComponent<IAccessibleFieldInputProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
18
21
|
export {};
|
|
@@ -1 +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
|
|
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;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB,yHAmG/B,CAAC"}
|
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// (C) 2026 GoodData Corporation
|
|
3
3
|
import { forwardRef, useCallback, useContext, useEffect, } from "react";
|
|
4
4
|
import { isArrowDownKey, isArrowKey } from "@gooddata/sdk-ui-kit";
|
|
5
5
|
import { PeriodRangeAccessibilityContext } from "./periodRangePickerAccessibility.js";
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* and focus behavior all still belong to rc-picker; this component just decorates the props it's handed.
|
|
7
|
+
* Replaces the plain `<input>` rc-picker renders for each half of the date range, adding the accessibility
|
|
8
|
+
* attributes our a11y model needs - a label, an invalid state, and a description pointing at a format hint
|
|
9
|
+
* or an error - plus that field's own leading calendar icon. Typing, masking and focus stay with rc-picker.
|
|
11
10
|
*
|
|
12
11
|
* @remarks
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* The icon is rendered here because rc-picker's own icon props render once for the whole range control,
|
|
13
|
+
* not once per field. Returning a fragment is safe: rc-picker reaches the input through `ref`.
|
|
14
|
+
* The icon is inert - `aria-hidden` plus `pointer-events: none` - so it adds no tab stop, and it is
|
|
15
|
+
* positioned over the input rather than beside it, so a click on it activates this field.
|
|
16
|
+
*
|
|
17
|
+
* Two facts are only observable here: whether the typed text failed to parse, and whether the field is
|
|
18
|
+
* currently blank. Both are reported upward raw, via an effect; the surrounding picker turns them into an
|
|
19
|
+
* error kind or message.
|
|
17
20
|
*/
|
|
18
21
|
export const AccessibleFieldInput = forwardRef((props, ref) => {
|
|
19
22
|
const { "date-range": side, "aria-invalid": rcPickerInvalid, onKeyDown, ...restProps } = props;
|
|
@@ -86,6 +89,6 @@ export const AccessibleFieldInput = forwardRef((props, ref) => {
|
|
|
86
89
|
onRequestOpen?.();
|
|
87
90
|
}
|
|
88
91
|
}, [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 }));
|
|
92
|
+
return (_jsxs(_Fragment, { children: [_jsx("span", { className: "gd-icon-calendar", "aria-hidden": "true" }), _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
93
|
});
|
|
91
94
|
AccessibleFieldInput.displayName = "AccessibleFieldInput";
|
|
@@ -2,9 +2,9 @@ import { type PeriodRangeSide } from "./periodRangePickerAccessibility.js";
|
|
|
2
2
|
import { type IPeriodRange, type IPeriodRangePickerProps, type PeriodRangePickerGranularity } from "./types.js";
|
|
3
3
|
export declare const DATE_FNS_PICKER_FORMATS: {
|
|
4
4
|
readonly fieldDateFormat: "yyyy-MM-dd";
|
|
5
|
-
readonly fieldWeekFormat: "
|
|
6
|
-
readonly fieldMonthFormat: "yyyy
|
|
7
|
-
readonly fieldQuarterFormat: "yyyy
|
|
5
|
+
readonly fieldWeekFormat: "w/YYYY";
|
|
6
|
+
readonly fieldMonthFormat: "M/yyyy";
|
|
7
|
+
readonly fieldQuarterFormat: "QQQ/yyyy";
|
|
8
8
|
readonly fieldYearFormat: "yyyy";
|
|
9
9
|
readonly fieldDateTimeFormat: "yyyy-MM-dd HH:mm";
|
|
10
10
|
readonly fieldTimeFormat: "HH:mm";
|
|
@@ -15,6 +15,7 @@ export declare const DATE_FNS_PICKER_FORMATS: {
|
|
|
15
15
|
readonly dayFormat: "d";
|
|
16
16
|
readonly cellMeridiemFormat: "a";
|
|
17
17
|
};
|
|
18
|
+
export declare const PREVIEW_ANNOUNCEMENT_DELAY = 1000;
|
|
18
19
|
/**
|
|
19
20
|
* Resolves the picker's raw selected [start, end] into the exact day-level range this component reports.
|
|
20
21
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PeriodRangePickerImpl.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/PeriodRangePicker/PeriodRangePickerImpl.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PeriodRangePickerImpl.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/PeriodRangePicker/PeriodRangePickerImpl.tsx"],"names":[],"mappings":"AAkDA,OAAO,EAKH,KAAK,eAAe,EAGvB,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACpC,MAAM,YAAY,CAAC;AAepB,eAAO,MAAM,uBAAuB;aAChC,eAAe,EAAE,YAAY;aAK7B,eAAe,EAAE,QAAQ;aACzB,gBAAgB,EAAE,QAAQ;aAC1B,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;AA2CX,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAsG/C;;;;;;;;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,2CAyazB"}
|
|
@@ -27,15 +27,17 @@ import * as rcZhTW from "@rc-component/picker/locale/zh_TW";
|
|
|
27
27
|
import { format, isValid, parse } from "date-fns";
|
|
28
28
|
import { defaultImport } from "default-import";
|
|
29
29
|
import { defineMessages, useIntl } from "react-intl";
|
|
30
|
-
import { useDebounce } from "@gooddata/sdk-ui";
|
|
30
|
+
import { useDebounce, useDebouncedState } from "@gooddata/sdk-ui";
|
|
31
31
|
import { useIdPrefixed } from "@gooddata/sdk-ui-kit";
|
|
32
|
-
import { platformDateFnsFormat } from "../constants/Platform.js";
|
|
32
|
+
import { DEFAULT_DATE_FORMAT, platformDateFnsFormat } from "../constants/Platform.js";
|
|
33
33
|
import { InputErrorMessage } from "../DateRangePicker/InputErrorMessage.js";
|
|
34
|
+
import { formatAbsoluteDateRange } from "../utils/FormattingUtils.js";
|
|
34
35
|
import { resolvePeriodBoundaries, resolvePeriodBoundary } from "../utils/StaticPeriodConversions.js";
|
|
36
|
+
import { getWeekStartDateFnsLocale, resolveWeekStartLocale } from "../utils/weekStartDateFnsLocale.js";
|
|
35
37
|
import { AccessibleFieldInput } from "./AccessibleFieldInput.js";
|
|
36
38
|
import { DateFnsRangePicker } from "./dateFnsRangePicker.js";
|
|
37
39
|
import { ERROR_MESSAGE_IDS, PeriodRangeAccessibilityContext, isBlockingFieldError, resolveFieldErrorKind, } from "./periodRangePickerAccessibility.js";
|
|
38
|
-
import {
|
|
40
|
+
import { PreviewAnnouncer } from "./PreviewAnnouncer.js";
|
|
39
41
|
// rc-picker is date-library agnostic and keeps its own field/header/cell format defaults in moment
|
|
40
42
|
// notation (see its `fillLocale`, mirrored in the comment on the guard test in
|
|
41
43
|
// tests/PeriodRangePicker.test.tsx). Since some tokens (e.g. "D", "Y") are legal in both moment and
|
|
@@ -49,12 +51,13 @@ import { getWeekStartDateFnsLocale, resolveWeekStartLocale } from "./weekStartDa
|
|
|
49
51
|
// everywhere beats per-locale chrome, at the cost of the year-panel suffix and French zero-padding.
|
|
50
52
|
export const DATE_FNS_PICKER_FORMATS = {
|
|
51
53
|
fieldDateFormat: "yyyy-MM-dd",
|
|
52
|
-
// Week-numbering year,
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
// Week-numbering year, not the calendar "yyyy": date-fns throws when a week-of-year token is
|
|
55
|
+
// combined with the calendar year, and needs its additional week-year tokens enabled for this one.
|
|
56
|
+
// The year, not the period, is what stays zero-padded: a half-typed year would otherwise parse and get
|
|
57
|
+
// re-spelled under the caret.
|
|
58
|
+
fieldWeekFormat: "w/YYYY",
|
|
59
|
+
fieldMonthFormat: "M/yyyy",
|
|
60
|
+
fieldQuarterFormat: "QQQ/yyyy",
|
|
58
61
|
fieldYearFormat: "yyyy",
|
|
59
62
|
// Never rendered (no time picker mode) but pinned anyway for a clean sweep - see the guard test.
|
|
60
63
|
fieldDateTimeFormat: "yyyy-MM-dd HH:mm",
|
|
@@ -66,16 +69,42 @@ export const DATE_FNS_PICKER_FORMATS = {
|
|
|
66
69
|
dayFormat: "d",
|
|
67
70
|
cellMeridiemFormat: "a",
|
|
68
71
|
};
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
// What each period field accepts when typed, handed to rc-picker as its `format` prop, which takes a
|
|
73
|
+
// list and outranks the locale's own per-granularity format. Only entry [0] is ever displayed - it is
|
|
74
|
+
// how a committed value is spelled and what the field snaps back to; the rest are parse-only
|
|
75
|
+
// alternates, tried in order.
|
|
76
|
+
//
|
|
77
|
+
// The displayed spelling is unpadded, the same way the backend spells these periods, so a field and a chart
|
|
78
|
+
// axis never name the same period differently. The alternates keep the padded spelling from being rejected
|
|
79
|
+
// outright; Quarter's runs the other way, accepting the bare quarter number. Quarter gets no "QQ/yyyy"
|
|
80
|
+
// alternate, so that a month typed into a quarter field errors instead of committing a different range.
|
|
81
|
+
//
|
|
82
|
+
// No alternate may pair a week-of-year token with a calendar year: date-fns throws on that combination
|
|
83
|
+
// and the parse loop has no try/catch, so one such entry would break parsing for that granularity's field.
|
|
84
|
+
const GRANULARITY_TO_FIELD_FORMATS = {
|
|
85
|
+
"GDC.time.week_us": [DATE_FNS_PICKER_FORMATS.fieldWeekFormat, "ww/YYYY"],
|
|
86
|
+
"GDC.time.month": [DATE_FNS_PICKER_FORMATS.fieldMonthFormat, "MM/yyyy"],
|
|
87
|
+
"GDC.time.quarter": [DATE_FNS_PICKER_FORMATS.fieldQuarterFormat, "Q/yyyy"],
|
|
88
|
+
"GDC.time.year": [DATE_FNS_PICKER_FORMATS.fieldYearFormat],
|
|
89
|
+
};
|
|
90
|
+
// The format shown to the user, in the hint panel and in the "invalid" error message, for granularities
|
|
91
|
+
// that have no user-configurable date format.
|
|
92
|
+
//
|
|
93
|
+
// Week deliberately does not reuse what its field actually parses: the field needs the week-numbering
|
|
94
|
+
// year, but spelling it that way here would read as a typo next to the other granularities, so all
|
|
95
|
+
// hints show the same year format. Do not collapse the two into one source - Week will stop parsing.
|
|
96
|
+
// The difference is invisible except in the week straddling a year boundary, where the two spellings
|
|
97
|
+
// pick different years and the hint names a value the field rejects - a rejection, never a wrong range.
|
|
98
|
+
const GRANULARITY_TO_HINT_FORMAT = {
|
|
99
|
+
"GDC.time.week_us": "w/yyyy",
|
|
73
100
|
"GDC.time.month": DATE_FNS_PICKER_FORMATS.fieldMonthFormat,
|
|
74
101
|
"GDC.time.quarter": DATE_FNS_PICKER_FORMATS.fieldQuarterFormat,
|
|
75
102
|
"GDC.time.year": DATE_FNS_PICKER_FORMATS.fieldYearFormat,
|
|
76
103
|
};
|
|
77
104
|
// A fixed sample date for the format hint's worked example
|
|
78
105
|
const HINT_EXAMPLE_DATE = new Date(2026, 2, 25);
|
|
106
|
+
// How long typing has to stop before the resolved range is announced.
|
|
107
|
+
export const PREVIEW_ANNOUNCEMENT_DELAY = 1000;
|
|
79
108
|
const messages = defineMessages({
|
|
80
109
|
dateFormatHint: { id: "filters.staticPeriod.dateFormatHint" },
|
|
81
110
|
dateFormatHintWithExample: { id: "filters.staticPeriod.dateFormatHintWithExample" },
|
|
@@ -334,20 +363,53 @@ export function PeriodRangePickerImpl({ granularity, range, onRangeChange, isMob
|
|
|
334
363
|
// When no per-workspace dateFormat is set, fall back to the same format the picker itself uses by
|
|
335
364
|
// default, so the hint text always matches what the picker actually expects. Once time granularity is
|
|
336
365
|
// supported, this fallback will need to switch too.
|
|
337
|
-
const
|
|
366
|
+
const hintFormat = granularity === "GDC.time.date"
|
|
338
367
|
? (dateFormat ?? DATE_FNS_PICKER_FORMATS.fieldDateFormat)
|
|
339
|
-
:
|
|
368
|
+
: GRANULARITY_TO_HINT_FORMAT[granularity];
|
|
369
|
+
// Day is left to the locale, which already carries the account's own date format.
|
|
370
|
+
const fieldFormats = granularity === "GDC.time.date" ? undefined : GRANULARITY_TO_FIELD_FORMATS[granularity];
|
|
340
371
|
// Independent of the `locale` useMemo below - also needed to render the format hint's worked example,
|
|
341
372
|
// which is computed for every non-Day granularity while `locale` only resolves this for Day/Week.
|
|
342
373
|
const weekStartLocaleKey = useMemo(() => getWeekStartDateFnsLocale(intl.locale, weekStart), [intl.locale, weekStart]);
|
|
343
|
-
// A worked example alongside the format token pattern (e.g. "yyyy
|
|
374
|
+
// A worked example alongside the format token pattern (e.g. "M/yyyy (e.g. 3/2026)"), so a caller who
|
|
344
375
|
// doesn't recognize date-fns tokens can still tell what to type.
|
|
345
376
|
const hintExample = useMemo(() => granularity === "GDC.time.date"
|
|
346
377
|
? undefined
|
|
347
|
-
: format(HINT_EXAMPLE_DATE,
|
|
378
|
+
: format(HINT_EXAMPLE_DATE, hintFormat, {
|
|
348
379
|
locale: resolveWeekStartLocale(weekStartLocaleKey),
|
|
349
380
|
useAdditionalWeekYearTokens: true,
|
|
350
|
-
}), [granularity,
|
|
381
|
+
}), [granularity, hintFormat, weekStartLocaleKey]);
|
|
382
|
+
// Day fields already spell out exact days, so a preview there would just repeat the inputs. Every other
|
|
383
|
+
// granularity shows a period token that hides which days the filter will actually cover.
|
|
384
|
+
const showRangePreview = granularity !== "GDC.time.date";
|
|
385
|
+
// Derived from `liveValue`, not from the committed `range` prop: after the first calendar cell click the
|
|
386
|
+
// committed range is still the previous one while the field already shows the new period, so a preview
|
|
387
|
+
// sourced from it would contradict the fields on screen. It carries period anchors, hence the same
|
|
388
|
+
// per-side boundary expansion the blur commit does.
|
|
389
|
+
//
|
|
390
|
+
// Undefined when there is no range to show; the en dash the row falls back to keeps it - and so the
|
|
391
|
+
// dialog height - stable, and is the character a formatted range joins its ends with.
|
|
392
|
+
//
|
|
393
|
+
// `liveValue` only holds the last successfully parsed dates, so a blank or unparsable field leaves stale
|
|
394
|
+
// ones behind. Without the validity guard the preview would contradict the error shown right above it;
|
|
395
|
+
// reversed ends are excluded for the same reason, since that range can never be applied.
|
|
396
|
+
const previewRange = useMemo(() => {
|
|
397
|
+
const [start, end] = liveValue;
|
|
398
|
+
if (!isRangeValid || isDateOrderError || !start || !end) {
|
|
399
|
+
return undefined;
|
|
400
|
+
}
|
|
401
|
+
return formatAbsoluteDateRange(resolveSelectedBoundary(granularity, start, "start", weekStart), resolveSelectedBoundary(granularity, end, "end", weekStart),
|
|
402
|
+
// Formatted like the filter button's own title, so it falls back to the same format that does.
|
|
403
|
+
dateFormat ?? DEFAULT_DATE_FORMAT);
|
|
404
|
+
}, [isRangeValid, isDateOrderError, liveValue, granularity, weekStart, dateFormat]);
|
|
405
|
+
// Announced from its own region rather than from the preview, which changes on every keystroke and would
|
|
406
|
+
// be read over the characters still being typed. Debouncing holds the announcement until typing stops.
|
|
407
|
+
const [, setPreviewAnnouncement, previewAnnouncement] = useDebouncedState(null, PREVIEW_ANNOUNCEMENT_DELAY);
|
|
408
|
+
useEffect(() => {
|
|
409
|
+
setPreviewAnnouncement(showRangePreview && previewRange !== undefined
|
|
410
|
+
? intl.formatMessage({ id: "filters.staticPeriod.rangePreview" }, { range: previewRange })
|
|
411
|
+
: null);
|
|
412
|
+
}, [showRangePreview, previewRange, intl, setPreviewAnnouncement]);
|
|
351
413
|
// Enter applies the whole range immediately in INDIVIDUAL mode, mirroring the classic picker's
|
|
352
414
|
// `onSubmitValue(true)`. It cannot hang off handleChange: rc-picker's onChange is a per-round event,
|
|
353
415
|
// so an Enter in the start field - before the end field has participated in the round - never
|
|
@@ -408,10 +470,10 @@ export function PeriodRangePickerImpl({ granularity, range, onRangeChange, isMob
|
|
|
408
470
|
return undefined;
|
|
409
471
|
}
|
|
410
472
|
if (kind === "invalid" && hintExample !== undefined) {
|
|
411
|
-
return intl.formatMessage({ id: INVALID_MESSAGE_IDS_WITH_EXAMPLE[side] }, { format:
|
|
473
|
+
return intl.formatMessage({ id: INVALID_MESSAGE_IDS_WITH_EXAMPLE[side] }, { format: hintFormat, example: hintExample });
|
|
412
474
|
}
|
|
413
475
|
const id = ERROR_MESSAGE_IDS[kind][side];
|
|
414
|
-
return intl.formatMessage({ id }, kind === "invalid" ? { format:
|
|
476
|
+
return intl.formatMessage({ id }, kind === "invalid" ? { format: hintFormat } : undefined);
|
|
415
477
|
};
|
|
416
478
|
// weekStart and dateFormat are workspace settings independent of display language, so Week/Day override the
|
|
417
479
|
// locale's own defaults for them (see weekStartDateFnsLocale.ts); Month/Quarter/Year need no override.
|
|
@@ -443,15 +505,15 @@ export function PeriodRangePickerImpl({ granularity, range, onRangeChange, isMob
|
|
|
443
505
|
const getPopupContainer = useCallback(() => wrapperRef.current ?? document.body, []);
|
|
444
506
|
return (_jsxs("div", { className: isMobile
|
|
445
507
|
? "gd-period-range-picker gd-period-range-picker-mobile s-period-range-picker"
|
|
446
|
-
: "gd-period-range-picker s-period-range-picker", ref: wrapperRef, children: [_jsx(PeriodRangeAccessibilityContext.Provider, { value: accessibility, children: _jsx(DateFnsRangePicker, { picker: pickerMode, value: value, onChange: handleChange, onCalendarChange: handleCalendarChange, onBlur: handleFieldBlur, open: open, onOpenChange: handleOpenChange, onClick: openCalendar, getPopupContainer: getPopupContainer, locale: locale, allowClear: false, order: false, allowEmpty: [true, true],
|
|
508
|
+
: "gd-period-range-picker s-period-range-picker", ref: wrapperRef, children: [_jsx(PeriodRangeAccessibilityContext.Provider, { value: accessibility, children: _jsx(DateFnsRangePicker, { picker: pickerMode, value: value, onChange: handleChange, onCalendarChange: handleCalendarChange, onBlur: handleFieldBlur, open: open, onOpenChange: handleOpenChange, onClick: openCalendar, getPopupContainer: getPopupContainer, locale: locale, format: fieldFormats, allowClear: false, order: false, allowEmpty: [true, true],
|
|
447
509
|
// Without this the picker overwrites a field's text with the
|
|
448
510
|
// formatted committed value the moment the field goes inactive - so text that
|
|
449
511
|
// failed to parse vanishes on blur and its "invalid format" error is replaced by
|
|
450
512
|
// "empty".
|
|
451
|
-
preserveInvalidOnBlur: true,
|
|
452
|
-
? intl.formatMessage(messages.dateFormatHint, { format:
|
|
513
|
+
preserveInvalidOnBlur: true, components: { input: AccessibleFieldInput } }) }), _jsxs("div", { id: hintId, className: "gd-period-range-picker__hint", children: [hintExample === undefined
|
|
514
|
+
? intl.formatMessage(messages.dateFormatHint, { format: hintFormat })
|
|
453
515
|
: intl.formatMessage(messages.dateFormatHintWithExample, {
|
|
454
|
-
format:
|
|
516
|
+
format: hintFormat,
|
|
455
517
|
example: hintExample,
|
|
456
|
-
}), customRangeHint] }), _jsx(InputErrorMessage, { descriptionId: startErrorId, errorText: getFieldErrorMessage("start") }), _jsx(InputErrorMessage, { descriptionId: endErrorId, errorText: getFieldErrorMessage("end") })] }));
|
|
518
|
+
}), customRangeHint] }), _jsx(InputErrorMessage, { descriptionId: startErrorId, errorText: getFieldErrorMessage("start") }), _jsx(InputErrorMessage, { descriptionId: endErrorId, errorText: getFieldErrorMessage("end") }), showRangePreview ? (_jsx("div", { className: "gd-period-range-picker__preview s-period-range-picker-preview", children: intl.formatMessage({ id: "filters.staticPeriod.rangePreview" }, { range: previewRange ?? "\u2013" }) })) : null, _jsx(PreviewAnnouncer, { message: previewAnnouncement })] }));
|
|
457
519
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How long a spent announcement lingers in the region before it is removed.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* It only has to be long enough that the removal cannot be mistaken for the replacement of a message that
|
|
6
|
+
* is still being announced - a screen reader takes its copy of the text when the DOM changes, so removing
|
|
7
|
+
* the node afterwards does not cut the speech short.
|
|
8
|
+
*/
|
|
9
|
+
export declare const ANNOUNCEMENT_LIFETIME = 3000;
|
|
10
|
+
interface IPreviewAnnouncerProps {
|
|
11
|
+
/**
|
|
12
|
+
* The message to announce. A new value is announced once; `null` announces nothing and leaves whatever
|
|
13
|
+
* is still pending to expire on its own.
|
|
14
|
+
*/
|
|
15
|
+
message: string | null;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A visually hidden live region that announces each message once and then empties itself.
|
|
19
|
+
*/
|
|
20
|
+
export declare function PreviewAnnouncer({ message }: IPreviewAnnouncerProps): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=PreviewAnnouncer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PreviewAnnouncer.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/PeriodRangePicker/PreviewAnnouncer.tsx"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAO1C,UAAU,sBAAsB;IAC5B;;;OAGG;IACH,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,OAAO,EAAE,EAAE,sBAAsB,2CAmCnE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// (C) 2026 GoodData Corporation
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
/**
|
|
5
|
+
* How long a spent announcement lingers in the region before it is removed.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* It only has to be long enough that the removal cannot be mistaken for the replacement of a message that
|
|
9
|
+
* is still being announced - a screen reader takes its copy of the text when the DOM changes, so removing
|
|
10
|
+
* the node afterwards does not cut the speech short.
|
|
11
|
+
*/
|
|
12
|
+
export const ANNOUNCEMENT_LIFETIME = 3000;
|
|
13
|
+
/**
|
|
14
|
+
* A visually hidden live region that announces each message once and then empties itself.
|
|
15
|
+
*/
|
|
16
|
+
export function PreviewAnnouncer({ message }) {
|
|
17
|
+
const [announcements, setAnnouncements] = useState([]);
|
|
18
|
+
const nextIdRef = useRef(0);
|
|
19
|
+
const timeoutsRef = useRef(new Set());
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!message) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const id = nextIdRef.current++;
|
|
25
|
+
setAnnouncements((current) => [...current, { id, text: message }]);
|
|
26
|
+
const timeout = window.setTimeout(() => {
|
|
27
|
+
timeoutsRef.current.delete(timeout);
|
|
28
|
+
setAnnouncements((current) => current.filter((announcement) => announcement.id !== id));
|
|
29
|
+
}, ANNOUNCEMENT_LIFETIME);
|
|
30
|
+
timeoutsRef.current.add(timeout);
|
|
31
|
+
}, [message]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const timeouts = timeoutsRef.current;
|
|
34
|
+
return () => {
|
|
35
|
+
timeouts.forEach((timeout) => window.clearTimeout(timeout));
|
|
36
|
+
timeouts.clear();
|
|
37
|
+
};
|
|
38
|
+
}, []);
|
|
39
|
+
return (_jsx("div", { className: "sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: announcements.map(({ id, text }) => (_jsx("div", { children: text }, id))) }));
|
|
40
|
+
}
|
|
@@ -5,7 +5,7 @@ import * as dateFnsGenerateConfigModule from "@rc-component/picker/generate/date
|
|
|
5
5
|
import { format as formatDate, getWeek, isValid, parse as parseDate, startOfWeek, } from "date-fns";
|
|
6
6
|
import * as dateFnsLocales from "date-fns/locale";
|
|
7
7
|
import { defaultImport } from "default-import";
|
|
8
|
-
import { resolveWeekStartLocale } from "
|
|
8
|
+
import { resolveWeekStartLocale } from "../utils/weekStartDateFnsLocale.js";
|
|
9
9
|
const baseGenerateConfig = defaultImport(dateFnsGenerateConfigModule);
|
|
10
10
|
// Mirrors the same (unexported) locale-key guessing logic the upstream generate config uses internally,
|
|
11
11
|
// so rc-picker's own locale bundles - keyed like "en_US" - keep resolving to a date-fns locale exactly as
|
|
@@ -25,7 +25,10 @@ export interface IPeriodRangePickerProps {
|
|
|
25
25
|
weekStart?: WeekStart;
|
|
26
26
|
withoutApply?: boolean;
|
|
27
27
|
submitForm: () => void;
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Date format (date-fns tokens). Formats the input fields for the "GDC.time.date" granularity, and the
|
|
30
|
+
* day-level range preview for all the others.
|
|
31
|
+
*/
|
|
29
32
|
dateFormat?: string;
|
|
30
33
|
/** Extra content rendered after the built-in format hint. */
|
|
31
34
|
customRangeHint?: ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/PeriodRangePicker/types.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAElG;;;GAGG;AACH,MAAM,MAAM,4BAA4B,GAAG,OAAO,CAC9C,qBAAqB,EACrB,eAAe,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,kBAAkB,GAAG,eAAe,CACjG,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,YAAY;IACzB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,EAAE,CAAC,EAAE,UAAU,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACpC,WAAW,EAAE,4BAA4B,CAAC;IAC1C,KAAK,EAAE,YAAY,CAAC;IACpB,aAAa,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;IAChD,QAAQ,EAAE,OAAO,CAAC;IAClB,uIAAuI;IACvI,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/PeriodRangePicker/types.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAElG;;;GAGG;AACH,MAAM,MAAM,4BAA4B,GAAG,OAAO,CAC9C,qBAAqB,EACrB,eAAe,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,kBAAkB,GAAG,eAAe,CACjG,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,YAAY;IACzB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,EAAE,CAAC,EAAE,UAAU,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACpC,WAAW,EAAE,4BAA4B,CAAC;IAC1C,KAAK,EAAE,YAAY,CAAC;IACpB,aAAa,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;IAChD,QAAQ,EAAE,OAAO,CAAC;IAClB,uIAAuI;IACvI,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACjD"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { mapOptionToAfm } from "./utils/AFMConversions.js";
|
|
2
|
+
import { formatAbsoluteDateRange } from "./utils/FormattingUtils.js";
|
|
2
3
|
import { filterVisibleDateFilterOptions } from "./utils/OptionUtils.js";
|
|
3
4
|
import { applyExcludeCurrentPeriod, canExcludeCurrentPeriod } from "./utils/PeriodExclusion.js";
|
|
4
|
-
import {
|
|
5
|
+
import { formatRelativeDateRange, getDateFilterRepresentation, getDateFilterTitleUsingTranslator } from "./utils/Translations/DateFilterTitle.js";
|
|
5
6
|
import { validateFilterOption } from "./validation/OptionValidation.js";
|
|
6
7
|
/**
|
|
7
8
|
* @beta
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/DateFilter/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAChG,OAAO,EACH,uBAAuB,EACvB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/DateFilter/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAChG,OAAO,EACH,uBAAuB,EACvB,2BAA2B,EAC3B,iCAAiC,EACpC,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;CAY7B,CAAC"}
|
package/esm/DateFilter/index.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
import { defaultDateFilterOptions } from "./constants/config.js";
|
|
3
3
|
import { granularityIntlCodes } from "./constants/i18n.js";
|
|
4
4
|
import { mapOptionToAfm } from "./utils/AFMConversions.js";
|
|
5
|
+
import { formatAbsoluteDateRange } from "./utils/FormattingUtils.js";
|
|
5
6
|
import { filterVisibleDateFilterOptions } from "./utils/OptionUtils.js";
|
|
6
7
|
import { applyExcludeCurrentPeriod, canExcludeCurrentPeriod } from "./utils/PeriodExclusion.js";
|
|
7
|
-
import {
|
|
8
|
+
import { formatRelativeDateRange, getDateFilterRepresentation, getDateFilterTitleUsingTranslator, } from "./utils/Translations/DateFilterTitle.js";
|
|
8
9
|
import { validateFilterOption } from "./validation/OptionValidation.js";
|
|
9
10
|
/**
|
|
10
11
|
* @beta
|
|
@@ -15,4 +15,8 @@ export declare const localizedIcuDateFormatPatterns: Record<string, string>;
|
|
|
15
15
|
* @internal
|
|
16
16
|
*/
|
|
17
17
|
export declare const getLocalizedIcuDateFormatPattern: (locale: string) => string;
|
|
18
|
+
/**
|
|
19
|
+
* @beta
|
|
20
|
+
*/
|
|
21
|
+
export declare const formatAbsoluteDateRange: (from: Date | string, to: Date | string, dateFormat: string, splitter?: string) => string;
|
|
18
22
|
//# sourceMappingURL=FormattingUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormattingUtils.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/utils/FormattingUtils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FormattingUtils.d.ts","sourceRoot":"","sources":["../../../src/DateFilter/utils/FormattingUtils.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,sBAAsB,WAAY,MAAM,KAAG,GAIvD,CAAC;AAEF,eAAO,MAAM,cAAc,UAAU,CAAC;AAEtC;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA2BjE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,WAAY,MAAM,WAC6B,CAAC;AAkC7F;;GAEG;AACH,eAAO,MAAM,uBAAuB,SAC1B,IAAI,GAAG,MAAM,MACf,IAAI,GAAG,MAAM,cACL,MAAM,wBAEnB,MAgCF,CAAC"}
|