1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.DatePickerBase = void 0;
|
4 | var tslib_1 = require("tslib");
|
5 | var React = require("react");
|
6 | var utilities_1 = require("@fluentui/utilities");
|
7 | var Calendar_1 = require("../../Calendar");
|
8 | var date_time_utilities_1 = require("@fluentui/date-time-utilities");
|
9 | var Callout_1 = require("../../Callout");
|
10 | var TextField_1 = require("../../TextField");
|
11 | var FocusTrapZone_1 = require("../../FocusTrapZone");
|
12 | var react_hooks_1 = require("@fluentui/react-hooks");
|
13 | var defaults_1 = require("./defaults");
|
14 | var getClassNames = utilities_1.classNamesFunction();
|
15 | var DEFAULT_PROPS = {
|
16 | allowTextInput: false,
|
17 | formatDate: function (date) { return (date ? date.toDateString() : ''); },
|
18 | parseDateFromString: function (dateStr) {
|
19 | var date = Date.parse(dateStr);
|
20 | return date ? new Date(date) : null;
|
21 | },
|
22 | firstDayOfWeek: date_time_utilities_1.DayOfWeek.Sunday,
|
23 | initialPickerDate: new Date(),
|
24 | isRequired: false,
|
25 | isMonthPickerVisible: true,
|
26 | showMonthPickerAsOverlay: false,
|
27 | strings: defaults_1.defaultDatePickerStrings,
|
28 | highlightCurrentMonth: false,
|
29 | highlightSelectedMonth: false,
|
30 | borderless: false,
|
31 | pickerAriaLabel: 'Calendar',
|
32 | showWeekNumbers: false,
|
33 | firstWeekOfYear: date_time_utilities_1.FirstWeekOfYear.FirstDay,
|
34 | showGoToToday: true,
|
35 | showCloseButton: false,
|
36 | underlined: false,
|
37 | allFocusable: false,
|
38 | };
|
39 | function useFocusLogic() {
|
40 | var textFieldRef = React.useRef(null);
|
41 | var preventFocusOpeningPicker = React.useRef(false);
|
42 | var focus = function () {
|
43 | var _a, _b;
|
44 | (_b = (_a = textFieldRef.current) === null || _a === void 0 ? void 0 : _a.focus) === null || _b === void 0 ? void 0 : _b.call(_a);
|
45 | };
|
46 | var preventNextFocusOpeningPicker = function () {
|
47 | preventFocusOpeningPicker.current = true;
|
48 | };
|
49 | return [textFieldRef, focus, preventFocusOpeningPicker, preventNextFocusOpeningPicker];
|
50 | }
|
51 | function useCalendarVisibility(_a, focus) {
|
52 | var allowTextInput = _a.allowTextInput, onAfterMenuDismiss = _a.onAfterMenuDismiss;
|
53 | var _b = React.useState(false), isCalendarShown = _b[0], setIsCalendarShown = _b[1];
|
54 | var isMounted = React.useRef(false);
|
55 | var async = react_hooks_1.useAsync();
|
56 | React.useEffect(function () {
|
57 | if (isMounted.current && !isCalendarShown) {
|
58 |
|
59 | if (allowTextInput) {
|
60 | async.requestAnimationFrame(focus);
|
61 | }
|
62 |
|
63 | onAfterMenuDismiss === null || onAfterMenuDismiss === void 0 ? void 0 : onAfterMenuDismiss();
|
64 | }
|
65 | isMounted.current = true;
|
66 |
|
67 | }, [isCalendarShown]);
|
68 | return [isCalendarShown, setIsCalendarShown];
|
69 | }
|
70 | function useSelectedDate(_a) {
|
71 | var formatDate = _a.formatDate, value = _a.value, onSelectDate = _a.onSelectDate;
|
72 | var _b = react_hooks_1.useControllableValue(value, undefined, function (ev, newValue) {
|
73 | return onSelectDate === null || onSelectDate === void 0 ? void 0 : onSelectDate(newValue);
|
74 | }), selectedDate = _b[0], setSelectedDateState = _b[1];
|
75 | var _c = React.useState(function () { return (value && formatDate ? formatDate(value) : ''); }), formattedDate = _c[0], setFormattedDate = _c[1];
|
76 | var setSelectedDate = function (newDate) {
|
77 | setSelectedDateState(newDate);
|
78 | setFormattedDate(newDate && formatDate ? formatDate(newDate) : '');
|
79 | };
|
80 | React.useEffect(function () {
|
81 | setFormattedDate(value && formatDate ? formatDate(value) : '');
|
82 | }, [formatDate, value]);
|
83 | return [selectedDate, formattedDate, setSelectedDate, setFormattedDate];
|
84 | }
|
85 | function useErrorMessage(_a, selectedDate, setSelectedDate, inputValue, isCalendarShown) {
|
86 | var isRequired = _a.isRequired, allowTextInput = _a.allowTextInput, strings = _a.strings, parseDateFromString = _a.parseDateFromString, onSelectDate = _a.onSelectDate, formatDate = _a.formatDate, minDate = _a.minDate, maxDate = _a.maxDate;
|
87 | var _b = React.useState(), errorMessage = _b[0], setErrorMessage = _b[1];
|
88 | var _c = React.useState(), statusMessage = _c[0], setStatusMessage = _c[1];
|
89 | var validateTextInput = function (date) {
|
90 | if (date === void 0) { date = null; }
|
91 | if (allowTextInput) {
|
92 | if (inputValue || date) {
|
93 |
|
94 |
|
95 |
|
96 | if (selectedDate && !errorMessage && formatDate && formatDate(date !== null && date !== void 0 ? date : selectedDate) === inputValue) {
|
97 | return;
|
98 | }
|
99 | date = date || parseDateFromString(inputValue);
|
100 |
|
101 | if (!date || isNaN(date.getTime())) {
|
102 |
|
103 | setSelectedDate(selectedDate);
|
104 |
|
105 | var selectedText = formatDate ? formatDate(selectedDate) : '';
|
106 | var statusText = strings.isResetStatusMessage
|
107 | ? utilities_1.format(strings.isResetStatusMessage, inputValue, selectedText)
|
108 | : strings.invalidInputErrorMessage || '';
|
109 | setStatusMessage(statusText);
|
110 | }
|
111 | else {
|
112 |
|
113 | if (isDateOutOfBounds(date, minDate, maxDate)) {
|
114 | setErrorMessage(strings.isOutOfBoundsErrorMessage || ' ');
|
115 | }
|
116 | else {
|
117 | setSelectedDate(date);
|
118 | setErrorMessage(undefined);
|
119 | setStatusMessage(undefined);
|
120 | }
|
121 | }
|
122 | }
|
123 | else {
|
124 |
|
125 | setErrorMessage(isRequired ? strings.isRequiredErrorMessage || ' ' : undefined);
|
126 |
|
127 |
|
128 | onSelectDate === null || onSelectDate === void 0 ? void 0 : onSelectDate(date);
|
129 | }
|
130 | }
|
131 | else if (isRequired && !inputValue) {
|
132 |
|
133 | setErrorMessage(strings.isRequiredErrorMessage || ' ');
|
134 | }
|
135 | else {
|
136 |
|
137 | setErrorMessage(undefined);
|
138 | setStatusMessage(undefined);
|
139 | }
|
140 | };
|
141 | React.useEffect(function () {
|
142 | if (isRequired && !selectedDate) {
|
143 | setErrorMessage(strings.isRequiredErrorMessage || ' ');
|
144 | }
|
145 | else if (selectedDate && isDateOutOfBounds(selectedDate, minDate, maxDate)) {
|
146 | setErrorMessage(strings.isOutOfBoundsErrorMessage || ' ');
|
147 | }
|
148 | else {
|
149 | setErrorMessage(undefined);
|
150 | }
|
151 |
|
152 | }, [
|
153 |
|
154 |
|
155 | minDate && date_time_utilities_1.getDatePartHashValue(minDate),
|
156 |
|
157 | maxDate && date_time_utilities_1.getDatePartHashValue(maxDate),
|
158 |
|
159 | selectedDate && date_time_utilities_1.getDatePartHashValue(selectedDate),
|
160 | isRequired,
|
161 | ]);
|
162 | return [
|
163 | isCalendarShown ? undefined : errorMessage,
|
164 | validateTextInput,
|
165 | setErrorMessage,
|
166 | isCalendarShown ? undefined : statusMessage,
|
167 | setStatusMessage,
|
168 | ];
|
169 | }
|
170 | exports.DatePickerBase = React.forwardRef(function (propsWithoutDefaults, forwardedRef) {
|
171 | var props = utilities_1.getPropsWithDefaults(DEFAULT_PROPS, propsWithoutDefaults);
|
172 | var firstDayOfWeek = props.firstDayOfWeek, strings = props.strings, label = props.label, theme = props.theme, className = props.className, styles = props.styles, initialPickerDate = props.initialPickerDate, isRequired = props.isRequired, disabled = props.disabled, ariaLabel = props.ariaLabel, pickerAriaLabel = props.pickerAriaLabel, placeholder = props.placeholder, allowTextInput = props.allowTextInput, borderless = props.borderless, minDate = props.minDate, maxDate = props.maxDate, showCloseButton = props.showCloseButton, calendarProps = props.calendarProps, calloutProps = props.calloutProps, textFieldProps = props.textField, underlined = props.underlined, allFocusable = props.allFocusable, _a = props.calendarAs, CalendarType = _a === void 0 ? Calendar_1.Calendar : _a, tabIndex = props.tabIndex, _b = props.disableAutoFocus, disableAutoFocus = _b === void 0 ? true : _b;
|
173 | var id = react_hooks_1.useId('DatePicker', props.id);
|
174 | var calloutId = react_hooks_1.useId('DatePicker-Callout');
|
175 | var calendar = React.useRef(null);
|
176 | var datePickerDiv = React.useRef(null);
|
177 | var _c = useFocusLogic(), textFieldRef = _c[0], focus = _c[1], preventFocusOpeningPicker = _c[2], preventNextFocusOpeningPicker = _c[3];
|
178 | var _d = useCalendarVisibility(props, focus), isCalendarShown = _d[0], setIsCalendarShown = _d[1];
|
179 | var _e = useSelectedDate(props), selectedDate = _e[0], formattedDate = _e[1], setSelectedDate = _e[2], setFormattedDate = _e[3];
|
180 | var _f = useErrorMessage(props, selectedDate, setSelectedDate, formattedDate, isCalendarShown), errorMessage = _f[0], validateTextInput = _f[1], setErrorMessage = _f[2], statusMessage = _f[3], setStatusMessage = _f[4];
|
181 | var showDatePickerPopup = React.useCallback(function () {
|
182 | if (!isCalendarShown) {
|
183 | preventNextFocusOpeningPicker();
|
184 | setIsCalendarShown(true);
|
185 | }
|
186 | }, [isCalendarShown, preventNextFocusOpeningPicker, setIsCalendarShown]);
|
187 | React.useImperativeHandle(props.componentRef, function () { return ({
|
188 | focus: focus,
|
189 | reset: function () {
|
190 | setIsCalendarShown(false);
|
191 | setSelectedDate(undefined);
|
192 | setErrorMessage(undefined);
|
193 | setStatusMessage(undefined);
|
194 | },
|
195 | showDatePickerPopup: showDatePickerPopup,
|
196 | }); }, [focus, setErrorMessage, setIsCalendarShown, setSelectedDate, setStatusMessage, showDatePickerPopup]);
|
197 | var onTextFieldFocus = function () {
|
198 | if (disableAutoFocus) {
|
199 | return;
|
200 | }
|
201 | if (!allowTextInput) {
|
202 | if (!preventFocusOpeningPicker.current) {
|
203 | showDatePickerPopup();
|
204 | }
|
205 | preventFocusOpeningPicker.current = false;
|
206 | }
|
207 | };
|
208 | var onSelectDate = function (date) {
|
209 | if (props.calendarProps && props.calendarProps.onSelectDate) {
|
210 | props.calendarProps.onSelectDate(date);
|
211 | }
|
212 | calendarDismissed(date);
|
213 | };
|
214 | var onCalloutPositioned = function () {
|
215 | var shouldFocus = true;
|
216 |
|
217 |
|
218 |
|
219 | if (props.calloutProps && props.calloutProps.setInitialFocus !== undefined) {
|
220 | shouldFocus = props.calloutProps.setInitialFocus;
|
221 | }
|
222 | if (calendar.current && shouldFocus) {
|
223 | calendar.current.focus();
|
224 | }
|
225 | };
|
226 | var onTextFieldBlur = function (ev) {
|
227 | validateTextInput();
|
228 | };
|
229 | var onTextFieldChanged = function (ev, newValue) {
|
230 | var _a;
|
231 | var textField = props.textField;
|
232 | if (allowTextInput) {
|
233 | if (isCalendarShown) {
|
234 | dismissDatePickerPopup();
|
235 | }
|
236 | setFormattedDate(newValue);
|
237 | }
|
238 | (_a = textField === null || textField === void 0 ? void 0 : textField.onChange) === null || _a === void 0 ? void 0 : _a.call(textField, ev, newValue);
|
239 | };
|
240 | var onTextFieldKeyDown = function (ev) {
|
241 |
|
242 | switch (ev.which) {
|
243 | case utilities_1.KeyCodes.enter:
|
244 | ev.preventDefault();
|
245 | ev.stopPropagation();
|
246 | if (!isCalendarShown) {
|
247 | validateTextInput();
|
248 | showDatePickerPopup();
|
249 | }
|
250 | else {
|
251 |
|
252 |
|
253 | if (props.allowTextInput) {
|
254 | dismissDatePickerPopup();
|
255 | }
|
256 | }
|
257 | break;
|
258 | case utilities_1.KeyCodes.escape:
|
259 | handleEscKey(ev);
|
260 | break;
|
261 | case utilities_1.KeyCodes.down:
|
262 | if (ev.altKey && !isCalendarShown) {
|
263 | showDatePickerPopup();
|
264 | }
|
265 | break;
|
266 | default:
|
267 | break;
|
268 | }
|
269 | };
|
270 | var onTextFieldClick = function (ev) {
|
271 |
|
272 | var openOnClick = props.openOnClick || !props.disableAutoFocus;
|
273 | if (openOnClick && !isCalendarShown && !props.disabled) {
|
274 | showDatePickerPopup();
|
275 | return;
|
276 | }
|
277 | if (props.allowTextInput) {
|
278 | dismissDatePickerPopup();
|
279 | }
|
280 | };
|
281 | var onIconClick = function (ev) {
|
282 | ev.stopPropagation();
|
283 | if (!isCalendarShown && !props.disabled) {
|
284 | showDatePickerPopup();
|
285 | }
|
286 | else if (props.allowTextInput) {
|
287 | dismissDatePickerPopup();
|
288 | }
|
289 | };
|
290 | var dismissDatePickerPopup = function (newlySelectedDate) {
|
291 | if (isCalendarShown) {
|
292 | setIsCalendarShown(false);
|
293 | validateTextInput(newlySelectedDate);
|
294 | if (!allowTextInput && newlySelectedDate) {
|
295 | setSelectedDate(newlySelectedDate);
|
296 | }
|
297 | }
|
298 | };
|
299 | var renderTextfieldDescription = function (inputProps, defaultRender) {
|
300 | return (React.createElement(React.Fragment, null,
|
301 | inputProps.description ? defaultRender(inputProps) : null,
|
302 | React.createElement("div", { "aria-live": "assertive", className: classNames.statusMessage }, statusMessage)));
|
303 | };
|
304 | var renderReadOnlyInput = function (inputProps) {
|
305 | var divProps = utilities_1.getNativeProps(inputProps, utilities_1.divProperties);
|
306 |
|
307 |
|
308 | return (React.createElement("div", tslib_1.__assign({}, divProps, { className: utilities_1.css(divProps.className, classNames.readOnlyTextField), tabIndex: tabIndex || 0 }), formattedDate || (
|
309 |
|
310 | React.createElement("span", { className: classNames.readOnlyPlaceholder }, placeholder))));
|
311 | };
|
312 | |
313 |
|
314 |
|
315 | var calendarDismissed = function (newlySelectedDate) {
|
316 | preventNextFocusOpeningPicker();
|
317 | dismissDatePickerPopup(newlySelectedDate);
|
318 |
|
319 | };
|
320 | var calloutDismissed = function (ev) {
|
321 | calendarDismissed();
|
322 | };
|
323 | var handleEscKey = function (ev) {
|
324 | if (isCalendarShown) {
|
325 | ev.stopPropagation();
|
326 | calendarDismissed();
|
327 | }
|
328 | };
|
329 | var classNames = getClassNames(styles, {
|
330 | theme: theme,
|
331 | className: className,
|
332 | disabled: disabled,
|
333 | underlined: underlined,
|
334 | label: !!label,
|
335 | isDatePickerShown: isCalendarShown,
|
336 | });
|
337 | var nativeProps = utilities_1.getNativeProps(props, utilities_1.divProperties, ['value']);
|
338 | var iconProps = textFieldProps && textFieldProps.iconProps;
|
339 | var textFieldId = textFieldProps && textFieldProps.id && textFieldProps.id !== id ? textFieldProps.id : id + '-label';
|
340 | var readOnly = !allowTextInput && !disabled;
|
341 | return (React.createElement("div", tslib_1.__assign({}, nativeProps, { className: classNames.root, ref: forwardedRef }),
|
342 | React.createElement("div", { ref: datePickerDiv, "aria-owns": isCalendarShown ? calloutId : undefined, className: classNames.wrapper },
|
343 | React.createElement(TextField_1.TextField, tslib_1.__assign({ role: "combobox", label: label, "aria-expanded": isCalendarShown, ariaLabel: ariaLabel, "aria-haspopup": "dialog", "aria-controls": isCalendarShown ? calloutId : undefined, required: isRequired, disabled: disabled, errorMessage: errorMessage, placeholder: placeholder, borderless: borderless, value: formattedDate, componentRef: textFieldRef, underlined: underlined, tabIndex: tabIndex, readOnly: !allowTextInput }, textFieldProps, { id: textFieldId, className: utilities_1.css(classNames.textField, textFieldProps && textFieldProps.className), iconProps: tslib_1.__assign(tslib_1.__assign({ iconName: 'Calendar' }, iconProps), { className: utilities_1.css(classNames.icon, iconProps && iconProps.className), onClick: onIconClick }),
|
344 |
|
345 | onRenderDescription: renderTextfieldDescription,
|
346 |
|
347 | onKeyDown: onTextFieldKeyDown,
|
348 |
|
349 | onFocus: onTextFieldFocus,
|
350 |
|
351 | onBlur: onTextFieldBlur,
|
352 |
|
353 | onClick: onTextFieldClick,
|
354 |
|
355 | onChange: onTextFieldChanged, onRenderInput: readOnly ? renderReadOnlyInput : undefined }))),
|
356 | isCalendarShown && (React.createElement(Callout_1.Callout, tslib_1.__assign({ id: calloutId, role: "dialog", ariaLabel: pickerAriaLabel, isBeakVisible: false, gapSpace: 0, doNotLayer: false, target: datePickerDiv.current, directionalHint: Callout_1.DirectionalHint.bottomLeftEdge }, calloutProps, { className: utilities_1.css(classNames.callout, calloutProps && calloutProps.className),
|
357 |
|
358 | onDismiss: calloutDismissed,
|
359 |
|
360 | onPositioned: onCalloutPositioned }),
|
361 | React.createElement(FocusTrapZone_1.FocusTrapZone, { isClickableOutsideFocusTrap: true, disableFirstFocus: disableAutoFocus },
|
362 | React.createElement(CalendarType, tslib_1.__assign({}, calendarProps, {
|
363 |
|
364 | onSelectDate: onSelectDate,
|
365 |
|
366 | onDismiss: calendarDismissed, isMonthPickerVisible: props.isMonthPickerVisible, showMonthPickerAsOverlay: props.showMonthPickerAsOverlay, today: props.today, value: selectedDate || initialPickerDate, firstDayOfWeek: firstDayOfWeek, strings: strings, highlightCurrentMonth: props.highlightCurrentMonth, highlightSelectedMonth: props.highlightSelectedMonth, showWeekNumbers: props.showWeekNumbers, firstWeekOfYear: props.firstWeekOfYear, showGoToToday: props.showGoToToday, dateTimeFormatter: props.dateTimeFormatter, minDate: minDate, maxDate: maxDate, componentRef: calendar, showCloseButton: showCloseButton, allFocusable: allFocusable })))))));
|
367 | });
|
368 | exports.DatePickerBase.displayName = 'DatePickerBase';
|
369 | function isDateOutOfBounds(date, minDate, maxDate) {
|
370 | return (!!minDate && date_time_utilities_1.compareDatePart(minDate, date) > 0) || (!!maxDate && date_time_utilities_1.compareDatePart(maxDate, date) < 0);
|
371 | }
|
372 |
|
\ | No newline at end of file |