@gooddata/sdk-ui-kit 11.52.0-alpha.4 → 11.52.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.
- package/esm/@ui/UiAsyncTable/UiAsyncTable/UiAsyncTableBody.d.ts.map +1 -1
- package/esm/@ui/UiAsyncTable/UiAsyncTable/UiAsyncTableBody.js +9 -6
- package/esm/@ui/UiAsyncTable/UiAsyncTable/UiAsyncTableRow.d.ts.map +1 -1
- package/esm/@ui/UiAsyncTable/UiAsyncTable/UiAsyncTableRow.js +13 -2
- package/esm/@ui/UiAsyncTable/UiAsyncTable/asyncTableGridContext.d.ts +4 -0
- package/esm/@ui/UiAsyncTable/UiAsyncTable/asyncTableGridContext.d.ts.map +1 -0
- package/esm/@ui/UiAsyncTable/UiAsyncTable/asyncTableGridContext.js +12 -0
- package/esm/@ui/UiCombobox/UiComboboxInput.d.ts +2 -0
- package/esm/@ui/UiCombobox/UiComboboxInput.d.ts.map +1 -1
- package/esm/@ui/UiCombobox/UiComboboxInput.js +2 -2
- package/esm/@ui/UiMenu/components/defaults/DefaultUiMenuInteractiveItem.d.ts.map +1 -1
- package/esm/@ui/UiMenu/components/defaults/DefaultUiMenuInteractiveItem.js +25 -22
- package/esm/@ui/UiMenu/types.d.ts +1 -0
- package/esm/@ui/UiMenu/types.d.ts.map +1 -1
- package/esm/@ui/UiTextInput/UiTextInput.d.ts +3 -1
- package/esm/@ui/UiTextInput/UiTextInput.d.ts.map +1 -1
- package/esm/@ui/UiTextInput/UiTextInput.js +2 -2
- package/esm/Datepicker/Datepicker.d.ts +3 -52
- package/esm/Datepicker/Datepicker.d.ts.map +1 -1
- package/esm/Datepicker/Datepicker.js +142 -180
- package/esm/Dialog/StylingEditorDialog/StylingEditorDialog.d.ts +9 -0
- package/esm/Dialog/StylingEditorDialog/StylingEditorDialog.d.ts.map +1 -1
- package/esm/Dialog/StylingEditorDialog/StylingEditorDialog.js +33 -28
- package/esm/Dropdown/DropdownButton.d.ts.map +1 -1
- package/esm/Dropdown/DropdownButton.js +5 -0
- package/esm/Form/InputWithNumberFormat.d.ts +0 -1
- package/esm/Form/InputWithNumberFormat.d.ts.map +1 -1
- package/esm/Form/InputWithNumberFormat.js +12 -18
- package/esm/Header/Header.d.ts.map +1 -1
- package/esm/Header/Header.js +2 -2
- package/esm/Header/HeaderChatButton.d.ts +9 -1
- package/esm/Header/HeaderChatButton.d.ts.map +1 -1
- package/esm/Header/HeaderChatButton.js +12 -1
- package/esm/Header/typings.d.ts +5 -0
- package/esm/Header/typings.d.ts.map +1 -1
- package/esm/Header/typings.js +1 -1
- package/esm/Timezone/TimezoneSelect.d.ts +10 -2
- package/esm/Timezone/TimezoneSelect.d.ts.map +1 -1
- package/esm/Timezone/TimezoneSelect.js +4 -3
- package/esm/index.d.ts +1 -1
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +1 -1
- package/esm/sdk-ui-kit.d.ts +37 -2
- package/package.json +11 -11
- package/src/@ui/UiMenu/UiMenu.scss +1 -1
- package/src/@ui/UiTextInput/UiTextInput.scss +7 -0
- package/styles/css/main.css +7 -1
- package/styles/css/main.css.map +1 -1
- package/styles/css/stylingEditorDialog.css +3 -0
- package/styles/css/stylingEditorDialog.css.map +1 -1
- package/styles/scss/stylingEditorDialog.scss +4 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// (C) 2020-2026 GoodData Corporation
|
|
3
|
-
import { PureComponent,
|
|
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";
|
|
@@ -81,191 +81,147 @@ function convertWeekStart(weekStart) {
|
|
|
81
81
|
throw new Error(`Unknown week start ${weekStart}`);
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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, intl, }) {
|
|
96
|
+
// these props are nullable at runtime (untyped callers do pass null explicitly), so plain default
|
|
97
|
+
// values are not enough - the class component guarded every usage with `||`, `??` or optional calls
|
|
98
|
+
const date = dateProp || DEFAULT_DATE;
|
|
99
|
+
const alignPoints = alignPointsProp ?? DEFAULT_ALIGN_POINTS;
|
|
100
|
+
const dateFormat = dateFormatProp ?? DEFAULT_DATE_FORMAT;
|
|
101
|
+
const weekStart = weekStartProp ?? "Sunday";
|
|
102
|
+
const rootRef = useRef(null);
|
|
103
|
+
const datePickerContainerRef = useRef(null);
|
|
104
|
+
const inputRef = useRef(null);
|
|
105
|
+
const datePickerId = useMemo(() => uuid(), []);
|
|
106
|
+
const [align, setAlign] = useState(alignPoints[0]?.align ?? "bl tl");
|
|
107
|
+
const [selectedDate, setSelectedDate] = useState(() => normalizeDate(date));
|
|
108
|
+
const [monthDate, setMonthDate] = useState(date);
|
|
109
|
+
const [inputValue, setInputValue] = useState(() => formatDate(date, dateFormat));
|
|
110
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
111
|
+
// resync the derived state whenever the date coming from the props changes
|
|
112
|
+
const [prevDateTime, setPrevDateTime] = useState(date.getTime());
|
|
113
|
+
if (date.getTime() !== prevDateTime) {
|
|
114
|
+
const newlySelectedDate = normalizeDate(date);
|
|
115
|
+
setPrevDateTime(date.getTime());
|
|
116
|
+
setSelectedDate(newlySelectedDate);
|
|
117
|
+
setMonthDate(newlySelectedDate);
|
|
118
|
+
setInputValue(formatDate(newlySelectedDate, dateFormat));
|
|
145
119
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
!this.inputRef.current.contains(event.target)) {
|
|
151
|
-
this.setState({ isOpen: false });
|
|
120
|
+
const alignDatePicker = useCallback(() => {
|
|
121
|
+
const container = datePickerContainerRef.current?.parentElement;
|
|
122
|
+
if (!alignPoints || !container || !rootRef.current) {
|
|
123
|
+
return;
|
|
152
124
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
125
|
+
const optimalAlignment = getOptimalAlignment({
|
|
126
|
+
targetRegion: elementRegion(rootRef.current),
|
|
127
|
+
selfRegion: elementRegion(container),
|
|
128
|
+
alignPoints,
|
|
129
|
+
});
|
|
130
|
+
const { align: optimalAlign } = optimalAlignment.alignment;
|
|
131
|
+
setAlign(optimalAlign);
|
|
132
|
+
onAlign?.(optimalAlign);
|
|
133
|
+
}, [alignPoints, onAlign]);
|
|
134
|
+
// keeps the debounced resize handler and the "just opened" effect stable while still
|
|
135
|
+
// calling the up-to-date alignment logic, the same way the class instance method did
|
|
136
|
+
const alignDatePickerRef = useRef(alignDatePicker);
|
|
137
|
+
// must run before the "just opened" layout effect below, hence useLayoutEffect
|
|
138
|
+
useLayoutEffect(() => {
|
|
139
|
+
alignDatePickerRef.current = alignDatePicker;
|
|
140
|
+
}, [alignDatePicker]);
|
|
141
|
+
const resizeHandler = useMemo(() => debounce(() => alignDatePickerRef.current(), 100), []);
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
window.addEventListener("resize", resizeHandler);
|
|
144
|
+
return () => {
|
|
145
|
+
resizeHandler.cancel();
|
|
146
|
+
window.removeEventListener("resize", resizeHandler);
|
|
147
|
+
};
|
|
148
|
+
}, [resizeHandler]);
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
const handleClickOutside = (event) => {
|
|
151
|
+
if (datePickerContainerRef.current &&
|
|
152
|
+
!datePickerContainerRef.current.contains(event.target) &&
|
|
153
|
+
inputRef.current &&
|
|
154
|
+
!inputRef.current.contains(event.target)) {
|
|
155
|
+
setIsOpen(false);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
159
|
+
return () => {
|
|
160
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
161
|
+
};
|
|
162
|
+
}, []);
|
|
163
|
+
useLayoutEffect(() => {
|
|
164
|
+
if (isOpen) {
|
|
165
|
+
alignDatePickerRef.current();
|
|
157
166
|
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
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) {
|
|
167
|
+
}, [isOpen]);
|
|
168
|
+
const handleInputBlur = useCallback((e) => {
|
|
169
|
+
onBlur?.(e.target.value);
|
|
170
|
+
}, [onBlur]);
|
|
171
|
+
const handleInputChanged = useCallback((e) => {
|
|
180
172
|
const { value } = e.target;
|
|
181
|
-
const parsedDate = parseDate(value,
|
|
182
|
-
|
|
183
|
-
|
|
173
|
+
const parsedDate = parseDate(value, dateFormat);
|
|
174
|
+
onValidateInput?.(value);
|
|
175
|
+
setInputValue(value);
|
|
184
176
|
if (parsedDate) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if (this.state.selectedDate) {
|
|
190
|
-
this.props.onChange?.(this.state.selectedDate);
|
|
191
|
-
}
|
|
192
|
-
});
|
|
177
|
+
setSelectedDate(parsedDate);
|
|
178
|
+
setMonthDate(parsedDate);
|
|
179
|
+
onChange?.(parsedDate);
|
|
180
|
+
return;
|
|
193
181
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
});
|
|
182
|
+
if (resetOnInvalidValue) {
|
|
183
|
+
setMonthDate(selectedDate);
|
|
184
|
+
return;
|
|
209
185
|
}
|
|
210
|
-
|
|
211
|
-
|
|
186
|
+
setSelectedDate(undefined);
|
|
187
|
+
setMonthDate(undefined);
|
|
188
|
+
// Signal invalid state by passing null
|
|
189
|
+
onChange?.(null);
|
|
190
|
+
}, [dateFormat, onChange, onValidateInput, resetOnInvalidValue, selectedDate]);
|
|
191
|
+
const handleDayChanged = useCallback((newlySelectedDate) => {
|
|
212
192
|
if (!newlySelectedDate) {
|
|
213
|
-
|
|
193
|
+
setIsOpen(false);
|
|
214
194
|
return;
|
|
215
195
|
}
|
|
216
|
-
if (
|
|
217
|
-
|
|
196
|
+
if (selectedDate && isSameDay(selectedDate, newlySelectedDate)) {
|
|
197
|
+
setIsOpen(false);
|
|
218
198
|
return;
|
|
219
199
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
this.setState({ monthDate: month });
|
|
234
|
-
}
|
|
235
|
-
handleCustomDayClick = (day, _modifiers) => {
|
|
200
|
+
inputRef.current?.focus();
|
|
201
|
+
onValidateInput?.(formatDate(newlySelectedDate, dateFormat));
|
|
202
|
+
setSelectedDate(newlySelectedDate);
|
|
203
|
+
setMonthDate(newlySelectedDate);
|
|
204
|
+
setInputValue(formatDate(newlySelectedDate, dateFormat));
|
|
205
|
+
setIsOpen(false);
|
|
206
|
+
onChange?.(newlySelectedDate);
|
|
207
|
+
}, [dateFormat, onChange, onValidateInput, selectedDate]);
|
|
208
|
+
const handleMonthChanged = useCallback((month) => {
|
|
209
|
+
inputRef.current?.focus();
|
|
210
|
+
setMonthDate(month);
|
|
211
|
+
}, []);
|
|
212
|
+
const handleCustomDayClick = useCallback((day, _modifiers) => {
|
|
236
213
|
// Handle all day clicks, including outside days
|
|
237
|
-
|
|
238
|
-
};
|
|
239
|
-
|
|
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) {
|
|
214
|
+
handleDayChanged(day);
|
|
215
|
+
}, [handleDayChanged]);
|
|
216
|
+
const handleKeyDown = useCallback((e) => {
|
|
261
217
|
if (e.key === "Escape" || e.key === "Tab") {
|
|
262
|
-
|
|
218
|
+
setIsOpen(false);
|
|
263
219
|
}
|
|
264
220
|
if (isEnterKey(e)) {
|
|
265
|
-
|
|
221
|
+
onDateInputKeyDown?.(e);
|
|
266
222
|
}
|
|
267
|
-
}
|
|
268
|
-
handleWrapperClick(e) {
|
|
223
|
+
}, [onDateInputKeyDown]);
|
|
224
|
+
const handleWrapperClick = useCallback((e) => {
|
|
269
225
|
const { classList } = e.target;
|
|
270
226
|
/**
|
|
271
227
|
* Prevent default fixes bug BB-332 but prevents in closing other dropdowns (Bug BB-1102)
|
|
@@ -274,19 +230,25 @@ export class WrappedDatePicker extends PureComponent {
|
|
|
274
230
|
if (e.target && classList?.contains(DATEPICKER_OUTSIDE_DAY_SELECTOR)) {
|
|
275
231
|
e.preventDefault();
|
|
276
232
|
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
233
|
+
}, []);
|
|
234
|
+
const handleInputClick = useCallback(() => {
|
|
235
|
+
setIsOpen(true);
|
|
236
|
+
}, []);
|
|
237
|
+
const dayPickerClassNames = useMemo(() => {
|
|
238
|
+
const [inputAnchorPoint, pickerAnchorPoint] = align.split(" ");
|
|
239
|
+
return {
|
|
240
|
+
root: classNames("gd-datepicker-picker", "gd-datepicker-OverlayWrapper", `gd-datepicker-OverlayWrapper-${inputAnchorPoint}-xx`, `gd-datepicker-OverlayWrapper-xx-${pickerAnchorPoint}`),
|
|
283
241
|
};
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
242
|
+
}, [align]);
|
|
243
|
+
const componentClasses = classNames("gd-datepicker", className, size, "gd-datepicker-input", isOpen ? "gd-datepicker-focused" : "");
|
|
244
|
+
const inputClasses = classNames("input-text", "small-12", size, `gd-datepicker-input-${datePickerId}`);
|
|
245
|
+
return (_jsxs("div", { "data-testid": "datepicker", className: componentClasses, ref: rootRef, onClick: handleWrapperClick, children: [
|
|
246
|
+
_jsx("input", { autoComplete: "off", "aria-labelledby": accessibilityConfig?.ariaLabelledBy, "aria-label": accessibilityConfig?.ariaLabel ||
|
|
247
|
+
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" })
|
|
248
|
+
] }));
|
|
289
249
|
}
|
|
250
|
+
export const WrappedDatePicker = memo(WrappedDatePickerCore);
|
|
251
|
+
WrappedDatePicker.displayName = "WrappedDatePicker";
|
|
290
252
|
const DatePickerWithIntl = injectIntl(WrappedDatePicker);
|
|
291
253
|
/**
|
|
292
254
|
* @internal
|
|
@@ -34,6 +34,15 @@ export interface IStylingEditorDialogProps<T extends StylingPickerItemContent> e
|
|
|
34
34
|
* submission and display it, or undefined when the content is valid.
|
|
35
35
|
*/
|
|
36
36
|
validateDefinition?: (content: T) => string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Optional non-blocking check of the parsed definition content.
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* Called only when the definition is valid JSON. Return a localized message to display it as a
|
|
42
|
+
* warning, or undefined when there is nothing to report. Unlike {@link IStylingEditorDialogProps.validateDefinition}
|
|
43
|
+
* this never prevents submission — use it for content that applies but degrades the result.
|
|
44
|
+
*/
|
|
45
|
+
validateDefinitionWarning?: (content: T) => string | undefined;
|
|
37
46
|
}
|
|
38
47
|
/**
|
|
39
48
|
* @internal
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StylingEditorDialog.d.ts","sourceRoot":"","sources":["../../../src/Dialog/StylingEditorDialog/StylingEditorDialog.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"StylingEditorDialog.d.ts","sourceRoot":"","sources":["../../../src/Dialog/StylingEditorDialog/StylingEditorDialog.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAQnF,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAEH,KAAK,+BAA+B,EACvC,MAAM,gCAAgC,CAAC;AAGxC;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,aAAa,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,wBAAwB;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB,CAAC,CAAC,SAAS,wBAAwB,CACzE,SAAQ,+BAA+B,EAAE,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,WAAW,CAAC;IACxF,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAC;IACnC,qBAAqB,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;IACxD;;;;;;;OAOG;IACH,yBAAyB,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;CAClE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,wBAAwB,EAAE,KAAK,EAAE,yBAAyB,CAAC,CAAC,CAAC,2CAM1G"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// (C) 2022-2026 GoodData Corporation
|
|
3
|
-
import { useMemo, useState } from "react";
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
4
|
import cx from "classnames";
|
|
5
5
|
import { useIntl } from "react-intl";
|
|
6
6
|
import { IntlWrapper } from "@gooddata/sdk-ui";
|
|
7
7
|
import { Button } from "../../Button/Button.js";
|
|
8
|
+
import { Message } from "../../Messages/Message.js";
|
|
8
9
|
import { Typography } from "../../Typography/Typography.js";
|
|
9
10
|
import { useId } from "../../utils/useId.js";
|
|
10
11
|
import { Dialog } from "../Dialog.js";
|
|
@@ -17,49 +18,48 @@ import { StylingExample } from "./StylingExample.js";
|
|
|
17
18
|
export function StylingEditorDialog(props) {
|
|
18
19
|
return (_jsx(IntlWrapper, { locale: props.locale, children: _jsx(StylingEditorDialogCore, { ...props }) }));
|
|
19
20
|
}
|
|
20
|
-
function StylingEditorDialogCore({ title, tooltip, link, stylingItem, examples, exampleToColorPreview, onClose, onSubmit, onCancel, disableSubmit, showProgressIndicator, showBackButton, onHelpClick, onExit = () => { }, className, onInvalidDefinition = () => { }, validateDefinition, }) {
|
|
21
|
+
function StylingEditorDialogCore({ title, tooltip, link, stylingItem, examples, exampleToColorPreview, onClose, onSubmit, onCancel, disableSubmit, showProgressIndicator, showBackButton, onHelpClick, onExit = () => { }, className, onInvalidDefinition = () => { }, validateDefinition, validateDefinitionWarning, }) {
|
|
21
22
|
const intl = useIntl();
|
|
22
23
|
const providedExamples = !!examples && examples.length !== 0 && !!exampleToColorPreview;
|
|
23
24
|
const initialNameField = stylingItem?.name ?? "";
|
|
24
25
|
const initialDefinitionField = stylingItem?.content ? JSON.stringify(stylingItem?.content, null, 4) : "";
|
|
25
26
|
const [nameField, setNameField] = useState(initialNameField);
|
|
26
27
|
const [definitionField, setDefinitionField] = useState(initialDefinitionField);
|
|
27
|
-
const
|
|
28
|
+
const parsedDefinition = useMemo(() => {
|
|
28
29
|
try {
|
|
29
|
-
|
|
30
|
-
const formattedDefinition = JSON.stringify(parsedDefinition, null, 4);
|
|
31
|
-
return nameField !== initialNameField || formattedDefinition !== initialDefinitionField;
|
|
30
|
+
return { ok: true, content: JSON.parse(definitionField) };
|
|
32
31
|
}
|
|
33
32
|
catch {
|
|
33
|
+
return { ok: false };
|
|
34
|
+
}
|
|
35
|
+
}, [definitionField]);
|
|
36
|
+
const fieldsChanged = useMemo(() => {
|
|
37
|
+
if (!parsedDefinition.ok) {
|
|
34
38
|
// initial state of the fields is presumed to be valid,
|
|
35
39
|
// so if JSON throws error, definition was changed
|
|
36
40
|
return true;
|
|
37
41
|
}
|
|
38
|
-
|
|
42
|
+
const formattedDefinition = JSON.stringify(parsedDefinition.content, null, 4);
|
|
43
|
+
return nameField !== initialNameField || formattedDefinition !== initialDefinitionField;
|
|
44
|
+
}, [parsedDefinition, nameField, initialNameField, initialDefinitionField]);
|
|
39
45
|
const validName = useMemo(() => nameField !== "", [nameField]);
|
|
40
|
-
const validDefinition =
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
catch {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
}, [definitionField]);
|
|
49
|
-
// Content-level validation (e.g. theme color values) runs only on well-formed JSON.
|
|
50
|
-
const definitionContentError = useMemo(() => {
|
|
51
|
-
if (!validateDefinition || !validDefinition) {
|
|
46
|
+
const validDefinition = parsedDefinition.ok;
|
|
47
|
+
// A callback that throws must not crash the dialog, so it degrades to "nothing to report" and
|
|
48
|
+
// the user can keep working.
|
|
49
|
+
const runContentCheck = useCallback((check, name) => {
|
|
50
|
+
if (!check || !parsedDefinition.ok) {
|
|
52
51
|
return undefined;
|
|
53
52
|
}
|
|
54
53
|
try {
|
|
55
|
-
return
|
|
54
|
+
return check(parsedDefinition.content);
|
|
56
55
|
}
|
|
57
56
|
catch (error) {
|
|
58
|
-
|
|
59
|
-
console.error("StylingEditorDialog: the provided validateDefinition callback threw.", error);
|
|
57
|
+
console.error(`StylingEditorDialog: the provided ${name} callback threw.`, error);
|
|
60
58
|
return undefined;
|
|
61
59
|
}
|
|
62
|
-
}, [
|
|
60
|
+
}, [parsedDefinition]);
|
|
61
|
+
const definitionContentError = useMemo(() => runContentCheck(validateDefinition, "validateDefinition"), [runContentCheck, validateDefinition]);
|
|
62
|
+
const definitionContentWarning = useMemo(() => runContentCheck(validateDefinitionWarning, "validateDefinitionWarning"), [runContentCheck, validateDefinitionWarning]);
|
|
63
63
|
const validFields = useMemo(() => validName && validDefinition && !definitionContentError, [validName, validDefinition, definitionContentError]);
|
|
64
64
|
const isSubmitDisabled = useMemo(() => !validFields || !fieldsChanged || disableSubmit, [validFields, fieldsChanged, disableSubmit]);
|
|
65
65
|
const emptyDefinition = useMemo(() => definitionField === "", [definitionField]);
|
|
@@ -89,10 +89,10 @@ function StylingEditorDialogCore({ title, tooltip, link, stylingItem, examples,
|
|
|
89
89
|
stylingItem?.ref,
|
|
90
90
|
intl,
|
|
91
91
|
]);
|
|
92
|
-
const getFinalStylingItem = (original,
|
|
92
|
+
const getFinalStylingItem = (original, content, name) => {
|
|
93
93
|
return {
|
|
94
94
|
...(original || {}),
|
|
95
|
-
content
|
|
95
|
+
content,
|
|
96
96
|
name,
|
|
97
97
|
};
|
|
98
98
|
};
|
|
@@ -111,13 +111,18 @@ function StylingEditorDialogCore({ title, tooltip, link, stylingItem, examples,
|
|
|
111
111
|
_jsxs("form", { className: "gd-styling-editor-dialog-content-form", onSubmit: (e) => e.preventDefault(), children: [
|
|
112
112
|
_jsxs("label", { className: "gd-styling-editor-dialog-content-form-input", children: [intl.formatMessage({ id: "stylingEditor.dialog.name" }), _jsx("input", { "aria-label": "Styling item name", className: "gd-input-field s-input-field", type: "text", value: nameField, onChange: (e) => setNameField(e.target.value) })
|
|
113
113
|
] }), _jsxs("label", { className: "gd-styling-editor-dialog-content-form-textarea", children: [intl.formatMessage({ id: "stylingEditor.dialog.definition" }), _jsx("textarea", { "aria-label": "Styling item definition", className: "gd-input-field s-textarea-field", wrap: "off", value: definitionField, onChange: (e) => setDefinitionField(e.target.value) })
|
|
114
|
-
] })
|
|
115
|
-
] }), providedExamples ? (_jsxs("div", { className: cx("gd-styling-editor-dialog-content-examples", "s-gd-styling-editor-dialog-content-examples"), children: [
|
|
114
|
+
] }), definitionContentWarning && !definitionContentError ? (_jsx(Message, { type: "warning", className: "gd-styling-editor-dialog-content-form-warning s-styling-editor-warning", children: definitionContentWarning })) : null] }), providedExamples ? (_jsxs("div", { className: cx("gd-styling-editor-dialog-content-examples", "s-gd-styling-editor-dialog-content-examples"), children: [
|
|
116
115
|
_jsx(BubbleHeaderSeparator, { title: intl.formatMessage({ id: "stylingEditor.dialog.examples" }), message: tooltip }), _jsx("div", { className: "gd-styling-editor-dialog-content-examples-list", children: examples.map((example, index) => (_jsx(StylingExample, { name: example.name ?? "", colors: exampleToColorPreview(example.content), onClick: () => {
|
|
117
116
|
setNameField(example.name ?? "");
|
|
118
117
|
setDefinitionField(JSON.stringify(example.content, null, 4));
|
|
119
118
|
} }, index))) })
|
|
120
|
-
] })) : null] }), _jsx(StylingEditorDialogFooter, { disableSubmit: isSubmitDisabled, showProgressIndicator: showProgressIndicator, link: link, errorMessage: errorMessage, onSubmit: () =>
|
|
119
|
+
] })) : null] }), _jsx(StylingEditorDialogFooter, { disableSubmit: isSubmitDisabled, showProgressIndicator: showProgressIndicator, link: link, errorMessage: errorMessage, onSubmit: () => {
|
|
120
|
+
// Submission is disabled while the definition does not parse, so this only
|
|
121
|
+
// narrows the type rather than guarding a reachable state.
|
|
122
|
+
if (parsedDefinition.ok) {
|
|
123
|
+
onSubmit?.(getFinalStylingItem(stylingItem, parsedDefinition.content, nameField));
|
|
124
|
+
}
|
|
125
|
+
}, onCancel: () => {
|
|
121
126
|
onExit?.(nameField, definitionField);
|
|
122
127
|
onCancel?.();
|
|
123
128
|
}, onHelpClick: onHelpClick })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownButton.d.ts","sourceRoot":"","sources":["../../src/Dropdown/DropdownButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AAMvF,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAE5E;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mBAAmB,CAAC,EAAE,wBAAwB,GAAG;QAAE,SAAS,CAAC,EAAE,cAAc,CAAC,eAAe,CAAC,CAAA;KAAE,CAAC;IAEjG,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IAElC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAC3B,EAAE,EACF,SAAS,EACT,mBAAmB,EAEnB,KAAK,EACL,KAAa,EACb,QAAQ,EAER,MAAM,EACN,OAAc,EACd,QAAQ,EACR,WAAmB,EACnB,OAAO,EACP,QAAQ,EACR,UAAU,EACV,SAAS,EACZ,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"DropdownButton.d.ts","sourceRoot":"","sources":["../../src/Dropdown/DropdownButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AAMvF,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAE5E;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mBAAmB,CAAC,EAAE,wBAAwB,GAAG;QAAE,SAAS,CAAC,EAAE,cAAc,CAAC,eAAe,CAAC,CAAA;KAAE,CAAC;IAEjG,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IAElC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAC3B,EAAE,EACF,SAAS,EACT,mBAAmB,EAEnB,KAAK,EACL,KAAa,EACb,QAAQ,EAER,MAAM,EACN,OAAc,EACd,QAAQ,EACR,WAAmB,EACnB,OAAO,EACP,QAAQ,EACR,UAAU,EACV,SAAS,EACZ,EAAE,oBAAoB,2CA4DtB"}
|
|
@@ -29,6 +29,11 @@ export function DropdownButton({ id, className, accessibilityConfig, value, titl
|
|
|
29
29
|
ariaLabel,
|
|
30
30
|
ariaLabelledBy,
|
|
31
31
|
ariaDescribedBy,
|
|
32
|
+
// Without a dropdownId the popup id is unknown, but the trigger is still a combobox,
|
|
33
|
+
// so it has to expose its expanded state - callers commonly pass only isOpen - along
|
|
34
|
+
// with any explicitly controlled id.
|
|
35
|
+
ariaExpanded: effectiveAriaExpanded,
|
|
36
|
+
ariaControls,
|
|
32
37
|
role: role ?? "combobox",
|
|
33
38
|
});
|
|
34
39
|
return (_jsx(Button, { id: id, accessibilityConfig: buttonAccessibilityConfig, title: title && typeof title === "string" ? title : undefined, className: buttonClasses, value: value, iconLeft: iconLeft, iconRight: isOpen ? "gd-icon-navigateup" : "gd-icon-navigatedown", disabled: disabled, onClick: onClick, ref: buttonRef, children: children }));
|
|
@@ -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;
|
|
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
|
-
|
|
13
|
-
const
|
|
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
|
|
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
|
-
|
|
27
|
+
const number = parseFloat(numberString);
|
|
28
|
+
return number === 0 ? 0 : number;
|
|
39
29
|
};
|
|
40
|
-
|
|
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
|
|
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);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Header.d.ts","sourceRoot":"","sources":["../../src/Header/Header.tsx"],"names":[],"mappings":"AA2BA,OAAO,EAAE,KAAK,eAAe,EAA8C,MAAM,cAAc,CAAC;AAyBhG;;GAEG;AACH,eAAO,MAAM,SAAS,
|
|
1
|
+
{"version":3,"file":"Header.d.ts","sourceRoot":"","sources":["../../src/Header/Header.tsx"],"names":[],"mappings":"AA2BA,OAAO,EAAE,KAAK,eAAe,EAA8C,MAAM,cAAc,CAAC;AAyBhG;;GAEG;AACH,eAAO,MAAM,SAAS,kGAskBpB,CAAC"}
|