@gooddata/sdk-ui-kit 11.52.0-alpha.5 → 11.52.0-alpha.7

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 (46) hide show
  1. package/esm/@ui/UiAddGranteeDialog/UiAddGranteeDialogCard.d.ts +8 -1
  2. package/esm/@ui/UiAddGranteeDialog/UiAddGranteeDialogCard.d.ts.map +1 -1
  3. package/esm/@ui/UiAddGranteeDialog/UiAddGranteeDialogCard.js +3 -3
  4. package/esm/@ui/UiAddGranteeDialog/useGranteeSelection.d.ts +2 -0
  5. package/esm/@ui/UiAddGranteeDialog/useGranteeSelection.d.ts.map +1 -1
  6. package/esm/@ui/UiAddGranteeDialog/useGranteeSelection.js +4 -1
  7. package/esm/@ui/UiCombobox/UiComboboxInput.d.ts +2 -0
  8. package/esm/@ui/UiCombobox/UiComboboxInput.d.ts.map +1 -1
  9. package/esm/@ui/UiCombobox/UiComboboxInput.js +2 -2
  10. package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.d.ts +18 -1
  11. package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.d.ts.map +1 -1
  12. package/esm/@ui/UiGranteeAsyncPicker/UiGranteeAsyncPicker.js +7 -2
  13. package/esm/@ui/UiGranteeRowControls/UiGranteeRowControls.d.ts +14 -5
  14. package/esm/@ui/UiGranteeRowControls/UiGranteeRowControls.d.ts.map +1 -1
  15. package/esm/@ui/UiGranteeRowControls/UiGranteeRowControls.js +8 -7
  16. package/esm/@ui/UiMenu/components/defaults/DefaultUiMenuInteractiveItem.d.ts.map +1 -1
  17. package/esm/@ui/UiMenu/components/defaults/DefaultUiMenuInteractiveItem.js +25 -22
  18. package/esm/@ui/UiMenu/types.d.ts +1 -0
  19. package/esm/@ui/UiMenu/types.d.ts.map +1 -1
  20. package/esm/@ui/UiPermissionMenu/UiPermissionMenu.d.ts +9 -1
  21. package/esm/@ui/UiPermissionMenu/UiPermissionMenu.d.ts.map +1 -1
  22. package/esm/@ui/UiPermissionMenu/UiPermissionMenu.js +8 -4
  23. package/esm/@ui/UiTextInput/UiTextInput.d.ts +3 -1
  24. package/esm/@ui/UiTextInput/UiTextInput.d.ts.map +1 -1
  25. package/esm/@ui/UiTextInput/UiTextInput.js +2 -2
  26. package/esm/ChartSorting/ChartSorting.d.ts +2 -14
  27. package/esm/ChartSorting/ChartSorting.d.ts.map +1 -1
  28. package/esm/ChartSorting/ChartSorting.js +4 -7
  29. package/esm/Datepicker/Datepicker.d.ts +5 -56
  30. package/esm/Datepicker/Datepicker.d.ts.map +1 -1
  31. package/esm/Datepicker/Datepicker.js +145 -183
  32. package/esm/Form/InputWithNumberFormat.d.ts +0 -1
  33. package/esm/Form/InputWithNumberFormat.d.ts.map +1 -1
  34. package/esm/Form/InputWithNumberFormat.js +12 -18
  35. package/esm/index.d.ts +2 -2
  36. package/esm/index.d.ts.map +1 -1
  37. package/esm/index.js +1 -1
  38. package/esm/locales.d.ts +3 -0
  39. package/esm/locales.d.ts.map +1 -1
  40. package/esm/locales.js +1 -0
  41. package/esm/sdk-ui-kit.d.ts +57 -28
  42. package/package.json +11 -11
  43. package/src/@ui/UiMenu/UiMenu.scss +1 -1
  44. package/src/@ui/UiTextInput/UiTextInput.scss +7 -0
  45. package/styles/css/main.css +4 -1
  46. package/styles/css/main.css.map +1 -1
@@ -2,12 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // (C) 2022-2026 GoodData Corporation
3
3
  import { useCallback, useMemo, useState } from "react";
4
4
  import { isEqual } from "lodash-es";
5
- import { FormattedMessage, injectIntl } from "react-intl";
5
+ import { FormattedMessage, useIntl } from "react-intl";
6
6
  import { IntlWrapper } from "@gooddata/sdk-ui";
7
7
  import { Button } from "../Button/Button.js";
8
8
  import { ChartSortingDropdown } from "./ChartSortingDropdown.js";
9
9
  import { ChartSortingDropdownBody } from "./ChartSortingDropdownBody.js";
10
- function ChartSorting({ currentSort, availableSorts, intl, bucketItems, buttonNode, onCancel, onApply, }) {
10
+ function ChartSorting({ currentSort, availableSorts, bucketItems, buttonNode, onCancel, onApply, }) {
11
+ const intl = useIntl();
11
12
  const [currentSelectedSort, setCurrentSort] = useState(currentSort);
12
13
  const handleApply = useCallback(() => {
13
14
  onApply(currentSelectedSort);
@@ -22,13 +23,9 @@ function ChartSorting({ currentSort, availableSorts, intl, bucketItems, buttonNo
22
23
  ] })
23
24
  ] }));
24
25
  }
25
- /**
26
- * @internal
27
- */
28
- export const ChartSortingWithIntl = injectIntl(ChartSorting);
29
26
  /**
30
27
  * @internal
31
28
  */
32
29
  export function ChartSortingDialog(props) {
33
- return (_jsx(IntlWrapper, { locale: props.locale, children: _jsx(ChartSortingWithIntl, { ...props }) }));
30
+ return (_jsx(IntlWrapper, { locale: props.locale, children: _jsx(ChartSorting, { ...props }) }));
34
31
  }
@@ -1,12 +1,11 @@
1
- import { PureComponent, type KeyboardEvent as ReactKeyboardEvent, type ReactNode } from "react";
2
- import { type WrappedComponentProps } from "react-intl";
1
+ import { PureComponent, type KeyboardEvent as ReactKeyboardEvent } from "react";
3
2
  import { type WeekStart } from "@gooddata/sdk-model";
4
3
  import { type IAccessibilityConfigBase } from "../typings/accessibility.js";
5
4
  import { type IAlignPoint } from "../typings/positioning.js";
6
5
  /**
7
6
  * @internal
8
7
  */
9
- export interface IDatePickerOwnProps {
8
+ export interface IDatePickerProps {
10
9
  accessibilityConfig?: IAccessibilityConfigBase;
11
10
  date?: Date;
12
11
  className?: string;
@@ -24,63 +23,13 @@ export interface IDatePickerOwnProps {
24
23
  weekStart?: WeekStart;
25
24
  onDateInputKeyDown?: (e: ReactKeyboardEvent<HTMLInputElement>) => void;
26
25
  }
27
- export type DatePickerProps = IDatePickerOwnProps & WrappedComponentProps;
28
- interface IDatePickerState {
29
- align: string;
30
- selectedDate: Date | undefined;
31
- monthDate: Date | undefined;
32
- isOpen: boolean;
33
- inputValue: string;
34
- }
35
26
  export declare function parseDate(str: string, dateFormat: string): Date | undefined;
36
- export declare class WrappedDatePicker extends PureComponent<DatePickerProps, IDatePickerState> {
37
- private rootRef;
38
- private datePickerContainerRef;
39
- private inputRef;
40
- private datePickerId;
41
- static defaultProps: {
42
- className: string;
43
- date: Date;
44
- placeholder: string;
45
- onChange: () => void;
46
- onBlur: () => void;
47
- resetOnInvalidValue: boolean;
48
- size: string;
49
- tabIndex: number;
50
- alignPoints: {
51
- align: string;
52
- }[];
53
- onAlign: () => void;
54
- dateFormat: string;
55
- weekStart: "Sunday";
56
- };
57
- constructor(props: DatePickerProps);
58
- componentDidMount(): void;
59
- UNSAFE_componentWillReceiveProps(nextProps: DatePickerProps): void;
60
- componentWillUnmount(): void;
61
- private handleClickOutside;
62
- componentDidUpdate(_prevProps: DatePickerProps, prevState: IDatePickerState): void;
63
- private setComponentRef;
64
- private getInputClasses;
65
- private getComponentClasses;
66
- private getOverlayWrapperClasses;
67
- resizeHandler: import("lodash-es").DebouncedFunc<() => void>;
68
- private updateDate;
69
- private handleInputBlur;
70
- private handleInputChanged;
71
- private handleDayChanged;
72
- private handleMonthChanged;
73
- private handleCustomDayClick;
74
- private normalizeDate;
75
- private alignDatePicker;
76
- private onKeyDown;
77
- private handleWrapperClick;
78
- render(): ReactNode;
79
- }
27
+ declare function WrappedDatePickerCore({ accessibilityConfig, date: dateProp, className, placeholder, onChange, onBlur, onValidateInput, resetOnInvalidValue, size, tabIndex, alignPoints: alignPointsProp, onAlign, dateFormat: dateFormatProp, weekStart: weekStartProp, onDateInputKeyDown }: IDatePickerProps): import("react/jsx-runtime").JSX.Element;
28
+ export declare const WrappedDatePicker: import("react").MemoExoticComponent<typeof WrappedDatePickerCore>;
80
29
  /**
81
30
  * @internal
82
31
  */
83
- export declare class Datepicker extends PureComponent<IDatePickerOwnProps> {
32
+ export declare class Datepicker extends PureComponent<IDatePickerProps> {
84
33
  render(): import("react/jsx-runtime").JSX.Element;
85
34
  }
86
35
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"Datepicker.d.ts","sourceRoot":"","sources":["../../src/Datepicker/Datepicker.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Datepicker.d.ts","sourceRoot":"","sources":["../../src/Datepicker/Datepicker.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGH,aAAa,EACb,KAAK,aAAa,IAAI,kBAAkB,EAS3C,MAAM,OAAO,CAAC;AAkCf,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAIrD,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAO7D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,mBAAmB,CAAC,EAAE,wBAAwB,CAAC;IAC/C,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,KAAK,IAAI,CAAC;IACxC,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,kBAAkB,CAAC,EAAE,CAAC,CAAC,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;CAC1E;AAuCD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAuB3E;AA2BD,iBAAS,qBAAqB,CAAC,EAC3B,mBAAmB,EACnB,IAAI,EAAE,QAAQ,EACd,SAAc,EACd,WAAgB,EAChB,QAAQ,EACR,MAAM,EACN,eAAe,EACf,mBAA2B,EAC3B,IAAS,EACT,QAAY,EACZ,WAAW,EAAE,eAAe,EAC5B,OAAO,EACP,UAAU,EAAE,cAAc,EAC1B,SAAS,EAAE,aAAa,EACxB,kBAAkB,EACrB,EAAE,gBAAgB,2CAuQlB;AAED,eAAO,MAAM,iBAAiB,mEAA8B,CAAC;AAG7D;;GAEG;AACH,qBAAa,UAAW,SAAQ,aAAa,CAAC,gBAAgB,CAAC;IAC3C,MAAM,4CAMrB;CACJ"}
@@ -1,12 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // (C) 2020-2026 GoodData Corporation
3
- import { PureComponent, createRef, } from "react";
3
+ import { PureComponent, memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
4
4
  import classNames from "classnames";
5
5
  import { format, isSameDay, isValid, parse } from "date-fns";
6
6
  import { de, enAU, enGB, enUS, es, fi, fr, frCA, id, it, ja, ko, nl, pl, pt, ptBR, ru, sl, th, tr, uk, vi, zhCN, } from "date-fns/locale";
7
7
  import { debounce } from "lodash-es";
8
8
  import { DayPicker } from "react-day-picker";
9
- import { injectIntl } from "react-intl";
9
+ import { useIntl } from "react-intl";
10
10
  import { v4 as uuid } from "uuid";
11
11
  import { IntlWrapper } from "@gooddata/sdk-ui";
12
12
  import { DEFAULT_DATE_FORMAT } from "../constants/platform.js";
@@ -81,191 +81,148 @@ function convertWeekStart(weekStart) {
81
81
  throw new Error(`Unknown week start ${weekStart}`);
82
82
  }
83
83
  }
84
- export class WrappedDatePicker extends PureComponent {
85
- rootRef = null;
86
- datePickerContainerRef = createRef();
87
- inputRef = createRef();
88
- datePickerId = uuid();
89
- static defaultProps = {
90
- className: "",
91
- date: new Date(),
92
- placeholder: "",
93
- onChange: () => { },
94
- onBlur: () => { },
95
- resetOnInvalidValue: false,
96
- size: "",
97
- tabIndex: 0,
98
- alignPoints: [{ align: "bl tl" }, { align: "br tr" }, { align: "tl bl" }, { align: "tr br" }],
99
- onAlign: () => { },
100
- dateFormat: DEFAULT_DATE_FORMAT,
101
- weekStart: "Sunday",
102
- };
103
- constructor(props) {
104
- super(props);
105
- const { alignPoints, date, dateFormat } = props;
106
- this.state = {
107
- align: alignPoints?.[0]?.align ?? "bl tl",
108
- selectedDate: date,
109
- monthDate: date,
110
- inputValue: formatDate(date || new Date(), dateFormat ?? DEFAULT_DATE_FORMAT),
111
- isOpen: false,
112
- };
113
- this.handleDayChanged = this.handleDayChanged.bind(this);
114
- this.handleMonthChanged = this.handleMonthChanged.bind(this);
115
- this.handleInputChanged = this.handleInputChanged.bind(this);
116
- this.handleInputBlur = this.handleInputBlur.bind(this);
117
- this.alignDatePicker = this.alignDatePicker.bind(this);
118
- this.setComponentRef = this.setComponentRef.bind(this);
119
- this.handleWrapperClick = this.handleWrapperClick.bind(this);
120
- this.handleClickOutside = this.handleClickOutside.bind(this);
121
- this.onKeyDown = this.onKeyDown.bind(this);
122
- this.handleCustomDayClick = this.handleCustomDayClick.bind(this);
123
- }
124
- componentDidMount() {
125
- const { date, dateFormat } = this.props;
126
- this.setState({ selectedDate: this.updateDate(date || new Date()) });
127
- this.setState({ inputValue: formatDate(date || new Date(), dateFormat ?? DEFAULT_DATE_FORMAT) });
128
- window.addEventListener("resize", this.resizeHandler);
129
- document.addEventListener("mousedown", this.handleClickOutside);
130
- }
131
- UNSAFE_componentWillReceiveProps(nextProps) {
132
- const { props } = this;
133
- const propsDate = props.date?.getTime() ?? 0;
134
- const nextPropsDate = nextProps.date?.getTime() ?? 0;
135
- if (propsDate !== nextPropsDate) {
136
- const selectedDate = this.updateDate(nextProps.date || new Date());
137
- this.setState({ selectedDate });
138
- this.setState({ monthDate: selectedDate });
139
- this.setState({ inputValue: formatDate(selectedDate, props.dateFormat ?? DEFAULT_DATE_FORMAT) });
140
- }
141
- }
142
- componentWillUnmount() {
143
- window.removeEventListener("resize", this.resizeHandler);
144
- document.removeEventListener("mousedown", this.handleClickOutside);
84
+ function normalizeDate(date) {
85
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
86
+ }
87
+ const DEFAULT_ALIGN_POINTS = [
88
+ { align: "bl tl" },
89
+ { align: "br tr" },
90
+ { align: "tl bl" },
91
+ { align: "tr br" },
92
+ ];
93
+ // evaluated once per module load, mirroring the original static defaultProps
94
+ const DEFAULT_DATE = new Date();
95
+ function WrappedDatePickerCore({ accessibilityConfig, date: dateProp, className = "", placeholder = "", onChange, onBlur, onValidateInput, resetOnInvalidValue = false, size = "", tabIndex = 0, alignPoints: alignPointsProp, onAlign, dateFormat: dateFormatProp, weekStart: weekStartProp, onDateInputKeyDown, }) {
96
+ const intl = useIntl();
97
+ // these props are nullable at runtime (untyped callers do pass null explicitly), so plain default
98
+ // values are not enough - the class component guarded every usage with `||`, `??` or optional calls
99
+ const date = dateProp || DEFAULT_DATE;
100
+ const alignPoints = alignPointsProp ?? DEFAULT_ALIGN_POINTS;
101
+ const dateFormat = dateFormatProp ?? DEFAULT_DATE_FORMAT;
102
+ const weekStart = weekStartProp ?? "Sunday";
103
+ const rootRef = useRef(null);
104
+ const datePickerContainerRef = useRef(null);
105
+ const inputRef = useRef(null);
106
+ const datePickerId = useMemo(() => uuid(), []);
107
+ const [align, setAlign] = useState(alignPoints[0]?.align ?? "bl tl");
108
+ const [selectedDate, setSelectedDate] = useState(() => normalizeDate(date));
109
+ const [monthDate, setMonthDate] = useState(date);
110
+ const [inputValue, setInputValue] = useState(() => formatDate(date, dateFormat));
111
+ const [isOpen, setIsOpen] = useState(false);
112
+ // resync the derived state whenever the date coming from the props changes
113
+ const [prevDateTime, setPrevDateTime] = useState(date.getTime());
114
+ if (date.getTime() !== prevDateTime) {
115
+ const newlySelectedDate = normalizeDate(date);
116
+ setPrevDateTime(date.getTime());
117
+ setSelectedDate(newlySelectedDate);
118
+ setMonthDate(newlySelectedDate);
119
+ setInputValue(formatDate(newlySelectedDate, dateFormat));
145
120
  }
146
- handleClickOutside(event) {
147
- if (this.datePickerContainerRef.current &&
148
- !this.datePickerContainerRef.current.contains(event.target) &&
149
- this.inputRef.current &&
150
- !this.inputRef.current.contains(event.target)) {
151
- this.setState({ isOpen: false });
121
+ const alignDatePicker = useCallback(() => {
122
+ const container = datePickerContainerRef.current?.parentElement;
123
+ if (!alignPoints || !container || !rootRef.current) {
124
+ return;
152
125
  }
153
- }
154
- componentDidUpdate(_prevProps, prevState) {
155
- if (this.state.isOpen && !prevState.isOpen) {
156
- this.alignDatePicker();
126
+ const optimalAlignment = getOptimalAlignment({
127
+ targetRegion: elementRegion(rootRef.current),
128
+ selfRegion: elementRegion(container),
129
+ alignPoints,
130
+ });
131
+ const { align: optimalAlign } = optimalAlignment.alignment;
132
+ setAlign(optimalAlign);
133
+ onAlign?.(optimalAlign);
134
+ }, [alignPoints, onAlign]);
135
+ // keeps the debounced resize handler and the "just opened" effect stable while still
136
+ // calling the up-to-date alignment logic, the same way the class instance method did
137
+ const alignDatePickerRef = useRef(alignDatePicker);
138
+ // must run before the "just opened" layout effect below, hence useLayoutEffect
139
+ useLayoutEffect(() => {
140
+ alignDatePickerRef.current = alignDatePicker;
141
+ }, [alignDatePicker]);
142
+ const resizeHandler = useMemo(() => debounce(() => alignDatePickerRef.current(), 100), []);
143
+ useEffect(() => {
144
+ window.addEventListener("resize", resizeHandler);
145
+ return () => {
146
+ resizeHandler.cancel();
147
+ window.removeEventListener("resize", resizeHandler);
148
+ };
149
+ }, [resizeHandler]);
150
+ useEffect(() => {
151
+ const handleClickOutside = (event) => {
152
+ if (datePickerContainerRef.current &&
153
+ !datePickerContainerRef.current.contains(event.target) &&
154
+ inputRef.current &&
155
+ !inputRef.current.contains(event.target)) {
156
+ setIsOpen(false);
157
+ }
158
+ };
159
+ document.addEventListener("mousedown", handleClickOutside);
160
+ return () => {
161
+ document.removeEventListener("mousedown", handleClickOutside);
162
+ };
163
+ }, []);
164
+ useLayoutEffect(() => {
165
+ if (isOpen) {
166
+ alignDatePickerRef.current();
157
167
  }
158
- }
159
- setComponentRef(ref) {
160
- this.rootRef = ref;
161
- }
162
- getInputClasses() {
163
- return classNames("input-text", "small-12", this.props.size, `gd-datepicker-input-${this.datePickerId}`);
164
- }
165
- getComponentClasses() {
166
- return classNames("gd-datepicker", this.props.className, this.props.size, "gd-datepicker-input", this.state.isOpen ? "gd-datepicker-focused" : "");
167
- }
168
- getOverlayWrapperClasses() {
169
- const [inputAnchorPoint, pickerAnchorPoint] = this.state.align.split(" ");
170
- return classNames("gd-datepicker-picker", "gd-datepicker-OverlayWrapper", `gd-datepicker-OverlayWrapper-${inputAnchorPoint}-xx`, `gd-datepicker-OverlayWrapper-xx-${pickerAnchorPoint}`);
171
- }
172
- resizeHandler = debounce(() => this.alignDatePicker(), 100);
173
- updateDate(date) {
174
- return this.normalizeDate(date);
175
- }
176
- handleInputBlur(e) {
177
- this.props.onBlur?.(e.target.value);
178
- }
179
- handleInputChanged(e) {
168
+ }, [isOpen]);
169
+ const handleInputBlur = useCallback((e) => {
170
+ onBlur?.(e.target.value);
171
+ }, [onBlur]);
172
+ const handleInputChanged = useCallback((e) => {
180
173
  const { value } = e.target;
181
- const parsedDate = parseDate(value, this.props.dateFormat ?? DEFAULT_DATE_FORMAT);
182
- this.props.onValidateInput?.(value);
183
- this.setState({ inputValue: value });
174
+ const parsedDate = parseDate(value, dateFormat);
175
+ onValidateInput?.(value);
176
+ setInputValue(value);
184
177
  if (parsedDate) {
185
- this.setState({
186
- selectedDate: parsedDate,
187
- monthDate: parsedDate,
188
- }, () => {
189
- if (this.state.selectedDate) {
190
- this.props.onChange?.(this.state.selectedDate);
191
- }
192
- });
178
+ setSelectedDate(parsedDate);
179
+ setMonthDate(parsedDate);
180
+ onChange?.(parsedDate);
181
+ return;
193
182
  }
194
- else {
195
- if (this.props.resetOnInvalidValue) {
196
- this.setState({
197
- selectedDate: this.state.selectedDate,
198
- monthDate: this.state.selectedDate,
199
- });
200
- return;
201
- }
202
- this.setState({
203
- selectedDate: undefined,
204
- monthDate: undefined,
205
- }, () => {
206
- // Signal invalid state by passing null
207
- this.props.onChange?.(null);
208
- });
183
+ if (resetOnInvalidValue) {
184
+ setMonthDate(selectedDate);
185
+ return;
209
186
  }
210
- }
211
- handleDayChanged(newlySelectedDate) {
187
+ setSelectedDate(undefined);
188
+ setMonthDate(undefined);
189
+ // Signal invalid state by passing null
190
+ onChange?.(null);
191
+ }, [dateFormat, onChange, onValidateInput, resetOnInvalidValue, selectedDate]);
192
+ const handleDayChanged = useCallback((newlySelectedDate) => {
212
193
  if (!newlySelectedDate) {
213
- this.setState({ isOpen: false });
194
+ setIsOpen(false);
214
195
  return;
215
196
  }
216
- if (this.state.selectedDate && isSameDay(this.state.selectedDate, newlySelectedDate)) {
217
- this.setState({ isOpen: false });
197
+ if (selectedDate && isSameDay(selectedDate, newlySelectedDate)) {
198
+ setIsOpen(false);
218
199
  return;
219
200
  }
220
- this.inputRef.current?.focus();
221
- this.props.onValidateInput?.(formatDate(newlySelectedDate, this.props.dateFormat ?? DEFAULT_DATE_FORMAT));
222
- this.setState({
223
- selectedDate: newlySelectedDate,
224
- monthDate: newlySelectedDate,
225
- inputValue: formatDate(newlySelectedDate, this.props.dateFormat ?? DEFAULT_DATE_FORMAT),
226
- isOpen: false,
227
- }, () => {
228
- this.props.onChange?.(newlySelectedDate);
229
- });
230
- }
231
- handleMonthChanged(month) {
232
- this.inputRef.current?.focus();
233
- this.setState({ monthDate: month });
234
- }
235
- handleCustomDayClick = (day, _modifiers) => {
201
+ inputRef.current?.focus();
202
+ onValidateInput?.(formatDate(newlySelectedDate, dateFormat));
203
+ setSelectedDate(newlySelectedDate);
204
+ setMonthDate(newlySelectedDate);
205
+ setInputValue(formatDate(newlySelectedDate, dateFormat));
206
+ setIsOpen(false);
207
+ onChange?.(newlySelectedDate);
208
+ }, [dateFormat, onChange, onValidateInput, selectedDate]);
209
+ const handleMonthChanged = useCallback((month) => {
210
+ inputRef.current?.focus();
211
+ setMonthDate(month);
212
+ }, []);
213
+ const handleCustomDayClick = useCallback((day, _modifiers) => {
236
214
  // Handle all day clicks, including outside days
237
- this.handleDayChanged(day);
238
- };
239
- normalizeDate(date) {
240
- return new Date(date.getFullYear(), date.getMonth(), date.getDate());
241
- }
242
- alignDatePicker() {
243
- const { alignPoints } = this.props;
244
- const container = this.datePickerContainerRef.current?.parentElement;
245
- if (!alignPoints || !container || !this.rootRef) {
246
- return;
247
- }
248
- const optimalAlignment = getOptimalAlignment({
249
- targetRegion: elementRegion(this.rootRef),
250
- selfRegion: elementRegion(container),
251
- alignPoints,
252
- });
253
- const { align } = optimalAlignment.alignment;
254
- this.setState({
255
- align,
256
- }, () => {
257
- this.props.onAlign?.(align);
258
- });
259
- }
260
- onKeyDown(e) {
215
+ handleDayChanged(day);
216
+ }, [handleDayChanged]);
217
+ const handleKeyDown = useCallback((e) => {
261
218
  if (e.key === "Escape" || e.key === "Tab") {
262
- this.setState({ isOpen: false });
219
+ setIsOpen(false);
263
220
  }
264
221
  if (isEnterKey(e)) {
265
- this.props.onDateInputKeyDown?.(e);
222
+ onDateInputKeyDown?.(e);
266
223
  }
267
- }
268
- handleWrapperClick(e) {
224
+ }, [onDateInputKeyDown]);
225
+ const handleWrapperClick = useCallback((e) => {
269
226
  const { classList } = e.target;
270
227
  /**
271
228
  * Prevent default fixes bug BB-332 but prevents in closing other dropdowns (Bug BB-1102)
@@ -274,25 +231,30 @@ export class WrappedDatePicker extends PureComponent {
274
231
  if (e.target && classList?.contains(DATEPICKER_OUTSIDE_DAY_SELECTOR)) {
275
232
  e.preventDefault();
276
233
  }
277
- }
278
- render() {
279
- const { inputValue, selectedDate, monthDate, isOpen } = this.state;
280
- const { accessibilityConfig, placeholder, intl, tabIndex } = this.props;
281
- const classNamesProps = {
282
- root: this.getOverlayWrapperClasses(),
234
+ }, []);
235
+ const handleInputClick = useCallback(() => {
236
+ setIsOpen(true);
237
+ }, []);
238
+ const dayPickerClassNames = useMemo(() => {
239
+ const [inputAnchorPoint, pickerAnchorPoint] = align.split(" ");
240
+ return {
241
+ root: classNames("gd-datepicker-picker", "gd-datepicker-OverlayWrapper", `gd-datepicker-OverlayWrapper-${inputAnchorPoint}-xx`, `gd-datepicker-OverlayWrapper-xx-${pickerAnchorPoint}`),
283
242
  };
284
- return (_jsxs("div", { "data-testid": "datepicker", className: this.getComponentClasses(), ref: this.setComponentRef, onClick: this.handleWrapperClick, children: [
285
- _jsx("input", { autoComplete: "off", "aria-labelledby": accessibilityConfig?.ariaLabelledBy, "aria-label": accessibilityConfig?.ariaLabel ||
286
- intl.formatMessage({ id: "datePicker.accessibility.label" }), "aria-describedby": accessibilityConfig?.ariaDescribedBy, onKeyDown: this.onKeyDown, tabIndex: tabIndex, onClick: () => this.setState({ isOpen: true }), ref: this.inputRef, value: inputValue, className: this.getInputClasses(), placeholder: placeholder, onChange: this.handleInputChanged, onBlur: this.handleInputBlur }), isOpen ? (_jsx("div", { id: `datepicker-popup-${this.datePickerId}`, role: "dialog", ref: this.datePickerContainerRef, children: _jsx(DayPicker, { classNames: classNamesProps, locale: convertLocale(intl.locale), showOutsideDays: true, mode: "single", selected: selectedDate, month: monthDate, onMonthChange: this.handleMonthChanged, weekStartsOn: convertWeekStart(this.props.weekStart ?? "Sunday"), onDayClick: this.handleCustomDayClick }) })) : null, _jsx("span", { className: "gd-datepicker-icon gd-icon-calendar" })
287
- ] }));
288
- }
243
+ }, [align]);
244
+ const componentClasses = classNames("gd-datepicker", className, size, "gd-datepicker-input", isOpen ? "gd-datepicker-focused" : "");
245
+ const inputClasses = classNames("input-text", "small-12", size, `gd-datepicker-input-${datePickerId}`);
246
+ return (_jsxs("div", { "data-testid": "datepicker", className: componentClasses, ref: rootRef, onClick: handleWrapperClick, children: [
247
+ _jsx("input", { autoComplete: "off", "aria-labelledby": accessibilityConfig?.ariaLabelledBy, "aria-label": accessibilityConfig?.ariaLabel ||
248
+ intl.formatMessage({ id: "datePicker.accessibility.label" }), "aria-describedby": accessibilityConfig?.ariaDescribedBy, onKeyDown: handleKeyDown, tabIndex: tabIndex, onClick: handleInputClick, ref: inputRef, value: inputValue, className: inputClasses, placeholder: placeholder, onChange: handleInputChanged, onBlur: handleInputBlur }), isOpen ? (_jsx("div", { id: `datepicker-popup-${datePickerId}`, role: "dialog", ref: datePickerContainerRef, children: _jsx(DayPicker, { classNames: dayPickerClassNames, locale: convertLocale(intl.locale), showOutsideDays: true, mode: "single", selected: selectedDate, month: monthDate, onMonthChange: handleMonthChanged, weekStartsOn: convertWeekStart(weekStart), onDayClick: handleCustomDayClick }) })) : null, _jsx("span", { className: "gd-datepicker-icon gd-icon-calendar" })
249
+ ] }));
289
250
  }
290
- const DatePickerWithIntl = injectIntl(WrappedDatePicker);
251
+ export const WrappedDatePicker = memo(WrappedDatePickerCore);
252
+ WrappedDatePicker.displayName = "WrappedDatePicker";
291
253
  /**
292
254
  * @internal
293
255
  */
294
256
  export class Datepicker extends PureComponent {
295
257
  render() {
296
- return (_jsx(IntlWrapper, { locale: this.props.locale, children: _jsx(DatePickerWithIntl, { ...this.props }) }));
258
+ return (_jsx(IntlWrapper, { locale: this.props.locale, children: _jsx(WrappedDatePicker, { ...this.props }) }));
297
259
  }
298
260
  }
@@ -1,7 +1,6 @@
1
1
  import { type ChangeEvent } from "react";
2
2
  import { type IInputPureProps } from "./InputPure.js";
3
3
  import { type Separators } from "./typings.js";
4
- export declare const MAX_NUMBER: number;
5
4
  /**
6
5
  * @internal
7
6
  */
@@ -1 +1 @@
1
- {"version":3,"file":"InputWithNumberFormat.d.ts","sourceRoot":"","sources":["../../src/Form/InputWithNumberFormat.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,WAAW,EAAsD,MAAM,OAAO,CAAC;AAI7F,OAAO,EAAE,KAAK,eAAe,EAAa,MAAM,gBAAgB,CAAC;AAEjE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/C,eAAO,MAAM,UAAU,QAAW,CAAC;AA6DnC;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC3C,UAAU,CAAC,EAAE,UAAU,CAAC;CAC3B;AAED;;GAEG;AAEH,MAAM,WAAW,2BAA2B;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AAEH,MAAM,WAAW,2BACb,SAAQ,8BAA8B,EAAE,IAAI,CAAC,eAAe,EAAE,UAAU,CAAC;IACzE,qGAAqG;IACrG,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;CAChF;AAUD;;GAEG;AACH,eAAO,MAAM,qBAAqB,mEAsEhC,CAAC"}
1
+ {"version":3,"file":"InputWithNumberFormat.d.ts","sourceRoot":"","sources":["../../src/Form/InputWithNumberFormat.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,WAAW,EAAsD,MAAM,OAAO,CAAC;AAI7F,OAAO,EAAE,KAAK,eAAe,EAAa,MAAM,gBAAgB,CAAC;AAEjE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAmD/C;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC3C,UAAU,CAAC,EAAE,UAAU,CAAC;CAC3B;AAED;;GAEG;AAEH,MAAM,WAAW,2BAA2B;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AAEH,MAAM,WAAW,2BACb,SAAQ,8BAA8B,EAAE,IAAI,CAAC,eAAe,EAAE,UAAU,CAAC;IACzE,qGAAqG;IACrG,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;CAChF;AAUD;;GAEG;AACH,eAAO,MAAM,qBAAqB,mEAsEhC,CAAC"}
@@ -4,20 +4,9 @@ import { memo, useEffect, useRef, useState } from "react";
4
4
  import { memoize } from "lodash-es";
5
5
  import { InputPure } from "./InputPure.js";
6
6
  import { DEFAULT_SEPARATORS, formatNumberWithSeparators } from "./numberFormat.js";
7
- // Highest number (BIGINT) according to gooddata documentation help.gooddata.com object-datatypes
8
- export const MAX_NUMBER = 10 ** 15;
9
- // Max number of digits right to decimal point according to gooddata documentation help.gooddata.com object-datatypes
10
- const MAX_DECIMAL_POINT_NUMBERS = 6;
11
7
  const getDanglingDecimalPointRegExp = memoize((decimal) => new RegExp(`\\${decimal}$`));
12
- const getFormatValidationRegExp = memoize(({ thousand, decimal }) => new RegExp(`^-?(\\d|\\${thousand})*(\\${decimal}\\d*)?$`));
13
- const parseStandardNumberString = (numberString) => {
14
- const belowDecimal = numberString.split(".")[1];
15
- const roundedNumberString = belowDecimal && belowDecimal.length >= MAX_DECIMAL_POINT_NUMBERS
16
- ? parseFloat(numberString).toFixed(MAX_DECIMAL_POINT_NUMBERS)
17
- : numberString;
18
- const number = parseFloat(roundedNumberString);
19
- return number === 0 ? 0 : number;
20
- };
8
+ // A half-typed exponent ("1e", "1e+") stays valid so it can be entered one character at a time.
9
+ const buildValidationRegExp = ({ thousand, decimal }) => new RegExp(`^-?(\\d|\\${thousand})*(\\${decimal}\\d*)?([eE][-+]?\\d*)?$`);
21
10
  // Removes thousand separators while keeping the decimal separator intact, so the value can be
22
11
  // edited as plain digits (no separators popping in/out) while the input is focused.
23
12
  const removeThousandSeparators = (value, { thousand } = DEFAULT_SEPARATORS) => value.split(thousand).join("");
@@ -27,7 +16,7 @@ const convertFormattedStringToStandard = (formattedString, { thousand, decimal }
27
16
  const withStandardDecimalPoint = withoutDanglingDecimalPoint.split(decimal).join(".");
28
17
  return withStandardDecimalPoint.length > 0 ? withStandardDecimalPoint : null;
29
18
  };
30
- const parse = (value, separators = DEFAULT_SEPARATORS) => {
19
+ const parse = (value, separators) => {
31
20
  if (value === null || value === "" || value === "-") {
32
21
  return null;
33
22
  }
@@ -35,11 +24,16 @@ const parse = (value, separators = DEFAULT_SEPARATORS) => {
35
24
  if (numberString === null) {
36
25
  return null;
37
26
  }
38
- return parseStandardNumberString(numberString);
27
+ const number = parseFloat(numberString);
28
+ return number === 0 ? 0 : number;
39
29
  };
40
- const isValid = (value, separators = DEFAULT_SEPARATORS) => {
30
+ // The wire layer draws the same line: it throws on Infinity/NaN.
31
+ const isValid = (value, separators) => {
32
+ if (!buildValidationRegExp(separators).test(value)) {
33
+ return false;
34
+ }
41
35
  const parsed = parse(value, separators);
42
- return getFormatValidationRegExp(separators).test(value) && Math.abs(parsed ?? 0) <= MAX_NUMBER;
36
+ return parsed === null || Number.isFinite(parsed);
43
37
  };
44
38
  // Coerces input value to number for formatting (handles string/number/null/undefined)
45
39
  const toNumberValue = (value) => {
@@ -51,7 +45,7 @@ const toNumberValue = (value) => {
51
45
  /**
52
46
  * @internal
53
47
  */
54
- export const InputWithNumberFormat = memo(function InputWithNumberFormat({ separators, value: propValue, onChange, onFocus, onBlur, ...restProps }) {
48
+ export const InputWithNumberFormat = memo(function InputWithNumberFormat({ separators = DEFAULT_SEPARATORS, value: propValue, onChange, onFocus, onBlur, ...restProps }) {
55
49
  const inputRef = useRef(null);
56
50
  const [value, setValue] = useState(() => formatNumberWithSeparators(toNumberValue(propValue), separators));
57
51
  const [isFocused, setIsFocused] = useState(false);
package/esm/index.d.ts CHANGED
@@ -34,7 +34,7 @@ export { type IYamlEditorProps, YamlEditor } from "./syntaxHighlightingInput/Yam
34
34
  export { type IYamlPosition, type YamlCompletionSource, yamlPositionAt, } from "./syntaxHighlightingInput/yamlPosition.js";
35
35
  export type { IDropdownButtonAccessibilityConfig, IButtonAccessibilityConfig, IButtonProps, } from "./Button/typings.js";
36
36
  export { Button } from "./Button/Button.js";
37
- export { Datepicker, type IDatePickerOwnProps } from "./Datepicker/Datepicker.js";
37
+ export { Datepicker, type IDatePickerProps } from "./Datepicker/Datepicker.js";
38
38
  export type { Separators, LabelSize } from "./Form/typings.js";
39
39
  export { Input, type IInputState } from "./Form/Input.js";
40
40
  export { InputPure, type IInputPureProps, type IInputPureAccessibilityConfig } from "./Form/InputPure.js";
@@ -305,7 +305,7 @@ export type { OnOpenedChange, IOnOpenedChangeParams, IMenuPositionConfig, MenuAl
305
305
  export type { IMenuStateProps, IMenuStateConfig } from "./Menu/MenuState.js";
306
306
  export type { IScrollGradientProps } from "./ScrollGradient/typings.js";
307
307
  export { ScrollGradient } from "./ScrollGradient/ScrollGradient.js";
308
- export { ChartSortingDialog, ChartSortingWithIntl, type IChartSortingOwnProps, type IChartSortingProps, } from "./ChartSorting/ChartSorting.js";
308
+ export { ChartSortingDialog, type IChartSortingProps } from "./ChartSorting/ChartSorting.js";
309
309
  export { SORT_TARGET_TYPE, type IAvailableSortsGroup, type MeasureSortSuggestion, type ISortTypeItem, type IBucketItemDescriptor, type IBucketItemDescriptors, } from "./ChartSorting/types.js";
310
310
  export { StylingSettingWidget, type IStylingSettingWidgetProps, } from "./SettingWidget/StylingSettingWidget/StylingSettingWidget.js";
311
311
  export { defaultThemeMetadataObject, defaultColorPaletteMetadataObject, getColorsPreviewFromTheme, getColorsPreviewFromColorPalette, } from "./SettingWidget/StylingSettingWidget/utils.js";