UNPKG

suomifi-ui-components

Version:
346 lines (343 loc) 15 kB
import { __rest, __assign, __makeTemplateObject } from 'tslib'; import React, { useRef, useState, useEffect, forwardRef } from 'react'; import { styled } from 'styled-components'; import classnames from 'classnames'; import { IconCalendar } from 'suomifi-icons'; import { AutoId } from '../../utils/AutoId/AutoId.js'; import { SuomifiThemeConsumer } from '../../theme/SuomifiThemeProvider/SuomifiThemeProvider.js'; import '../../theme/SuomifiTheme/SuomifiTheme.js'; import { SpacingConsumer } from '../../theme/SpacingProvider/SpacingProvider.js'; import { Debounce } from '../../utils/Debounce/Debounce.js'; import { getConditionalAriaProp } from '../../../utils/aria/aria.js'; import { getLogger } from '../../../utils/log/logger.js'; import { filterDuplicateKeys, forkRefs } from '../../../utils/common/common.js'; import '../../../reset/HtmlA/HtmlA.js'; import { HtmlButton } from '../../../reset/HtmlButton/HtmlButton.js'; import { HtmlDiv } from '../../../reset/HtmlDiv/HtmlDiv.js'; import '../../../reset/HtmlFieldSet/HtmlFieldSet.js'; import '../../../reset/HtmlH/HtmlH.js'; import { HtmlInput } from '../../../reset/HtmlInput/HtmlInput.js'; import '../../../reset/HtmlLabel/HtmlLabel.js'; import '../../../reset/HtmlLegend/HtmlLegend.js'; import '../../../reset/HtmlLi/HtmlLi.js'; import '../../../reset/HtmlNav/HtmlNav.js'; import '../../../reset/HtmlOl/HtmlOl.js'; import '../../../reset/HtmlSpan/HtmlSpan.js'; import '../../../reset/HtmlTextarea/HtmlTextarea.js'; import '../../../reset/HtmlUl/HtmlUl.js'; import '../../../reset/HtmlTable/HtmlTable.js'; import '../../../reset/HtmlTable/HtmlTableCaption.js'; import '../../../reset/HtmlTable/HtmlTableHeader.js'; import '../../../reset/HtmlTable/HtmlTableRow.js'; import '../../../reset/HtmlTable/HtmlTableBody.js'; import '../../../reset/HtmlTable/HtmlTableHeaderCell.js'; import '../../../reset/HtmlTable/HtmlTableCell.js'; import { DatePicker } from './DatePicker/DatePicker.js'; import { Label } from '../Label/Label.js'; import { StatusText } from '../StatusText/StatusText.js'; import { HintText } from '../HintText/HintText.js'; import { VisuallyHidden } from '../../VisuallyHidden/VisuallyHidden.js'; import { baseStyles } from './DateInput.baseStyles.js'; import { datePickerDefaultTexts, languages, defaultLanguage } from './datePickerTexts.js'; import { moveYears, firstDayOfMonth, lastDayOfMonth, cellDateAriaLabel, dayIsInRange, defaultDateAdapter } from './dateUtils.js'; import { separateMarginProps } from '../../theme/utils/spacing.js'; var baseClassName = 'fi-date-input'; var dateInputClassNames = { baseClassName: baseClassName, fullWidth: "".concat(baseClassName, "--full-width"), hasPicker: "".concat(baseClassName, "--has-picker"), disabled: "".concat(baseClassName, "--disabled"), error: "".concat(baseClassName, "--error"), success: "".concat(baseClassName, "--success"), labelIsVisible: "".concat(baseClassName, "_label--visible"), inputAndPickerWrapper: "".concat(baseClassName, "_input-and-picker-wrapper"), inputElementContainer: "".concat(baseClassName, "_input-element-container"), inputElement: "".concat(baseClassName, "_input"), pickerElementContainer: "".concat(baseClassName, "_picker-element-container"), pickerButton: "".concat(baseClassName, "_picker-button"), pickerButtonDisabled: "".concat(baseClassName, "_picker-button--disabled"), pickerIcon: "".concat(baseClassName, "_picker-icon"), styleWrapper: "".concat(baseClassName, "_wrapper"), statusTextHasContent: "".concat(baseClassName, "_statusText--has-content") }; var BaseDateInput = function BaseDateInput(props) { var _a, _b, _c, _d; var className = props.className, labelText = props.labelText, labelMode = props.labelMode, propOnChange = props.onChange, onDatePickerButtonBlur = props.onDatePickerButtonBlur, optionalText = props.optionalText, status = props.status, statusText = props.statusText, hintText = props.hintText, visualPlaceholder = props.visualPlaceholder, id = props.id, fullWidth = props.fullWidth, forwardedRef = props.forwardedRef, debounce = props.debounce, _e = props.statusTextAriaLiveMode, statusTextAriaLiveMode = _e === void 0 ? 'assertive' : _e, ariaDescribedBy = props["aria-describedby"], defaultValue = props.defaultValue, value = props.value, _f = props.datePickerEnabled, datePickerEnabled = _f === void 0 ? false : _f, customDatePickerProps = props.datePickerProps, _g = props.datePickerPosition, datePickerPosition = _g === void 0 ? 'left' : _g, _h = props.smallScreen, smallScreen = _h === void 0 ? false : _h, _j = props.datePickerTexts, datePickerTexts = _j === void 0 ? undefined : _j, _k = props.language, language = _k === void 0 ? defaultLanguage : _k, _l = props.dateAdapter, dateAdapter = _l === void 0 ? defaultDateAdapter() : _l, shouldDisableDate = props.shouldDisableDate, userMinDate = props.minDate, userMaxDate = props.maxDate, initialDate = props.initialDate, tooltipComponent = props.tooltipComponent, style = props.style, rest = __rest(props, ["className", "labelText", "labelMode", "onChange", "onDatePickerButtonBlur", "optionalText", "status", "statusText", "hintText", "visualPlaceholder", "id", "fullWidth", "forwardedRef", "debounce", "statusTextAriaLiveMode", 'aria-describedby', "defaultValue", "value", "datePickerEnabled", "datePickerProps", "datePickerPosition", "smallScreen", "datePickerTexts", "language", "dateAdapter", "shouldDisableDate", "minDate", "maxDate", "initialDate", "tooltipComponent", "style"]); var defaultMinDate = moveYears(firstDayOfMonth(new Date()), -10); var defaultMaxDate = moveYears(lastDayOfMonth(new Date()), 10); var userProvidedMinDate = userMinDate; var userProvidedMaxDate = userMaxDate; var effectiveMinDate; var effectiveMaxDate; if (userProvidedMinDate && userProvidedMaxDate) { effectiveMinDate = userProvidedMinDate; effectiveMaxDate = userProvidedMaxDate; } else if (userProvidedMinDate && !userProvidedMaxDate) { effectiveMinDate = userProvidedMinDate; effectiveMaxDate = userProvidedMinDate > defaultMaxDate ? moveYears(userProvidedMinDate, 10) : defaultMaxDate; } else if (!userProvidedMinDate && userProvidedMaxDate) { effectiveMaxDate = userProvidedMaxDate; effectiveMinDate = userProvidedMaxDate < defaultMinDate ? moveYears(userProvidedMaxDate, -10) : defaultMinDate; } else { effectiveMinDate = defaultMinDate; effectiveMaxDate = defaultMaxDate; } var _m = separateMarginProps(rest), passProps = _m[1]; var hintTextId = "".concat(id, "-hintText"); var statusTextId = "".concat(id, "-statusText"); var inputRef = useRef(); var openButtonRef = useRef(null); var _o = useState(''), inputValue = _o[0], setInputValue = _o[1]; var _p = useState(''), buttonDateLabel = _p[0], setButtonDateLabel = _p[1]; var _q = useState(null), inputValueAsDate = _q[0], setInputValueAsDate = _q[1]; var _r = useState(false), calendarVisible = _r[0], setCalendarVisible = _r[1]; var _s = useState(datePickerDefaultTexts.fi), texts = _s[0], setTexts = _s[1]; var definedRef = forwardedRef || null; useEffect(function () { if (defaultValue) { setInputValue(String(defaultValue)); } }, []); useEffect(function () { var useValue = 'value' in props && value ? value : inputValue; if (useValue) { var date = validateDatePickerValue(useValue); if (date) { setButtonDateLabel("".concat(texts.openButtonLabel, ", ").concat(texts.selectedDateLabel, " ").concat(cellDateAriaLabel(date, texts))); } } }, [value, inputValue]); useEffect(function () { var lang; if (languages.includes(language)) { lang = language; } else { lang = defaultLanguage; getLogger().warn("Language \"".concat(language, "\" is not supported")); } setTexts(__assign(__assign({}, datePickerDefaultTexts[lang]), datePickerTexts)); }, [language, datePickerTexts]); var toggleCalendar = function toggleCalendar(open, focus) { var _a, _b; if (focus === void 0) { focus = false; } setCalendarVisible(open); if (open) { updateInputValueAsDate(((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.value) || null); } if (!open && focus) { (_b = openButtonRef.current) === null || _b === void 0 ? void 0 : _b.focus(); } }; var conditionalSetInputValue = function conditionalSetInputValue(newValue) { if (!('value' in props)) { setInputValue(newValue); } }; var validateDatePickerValue = function validateDatePickerValue(newValue) { var date = dateAdapter.parse(newValue); if (Number.isNaN(date.valueOf())) { getLogger().warn("Date input value \"".concat(newValue, "\" could not be parsed to Date")); return null; } if (!dayIsInRange(date, effectiveMinDate, effectiveMaxDate)) { getLogger().warn("Date input value \"".concat(newValue, "\" is not within interval [minDate, maxDate]")); return null; } return date; }; var updateInputValueAsDate = function updateInputValueAsDate(newValue) { if (newValue === null) { setInputValueAsDate(newValue); } else { var date = validateDatePickerValue(newValue); setInputValueAsDate(date); } }; var onDatePickerChange = function onDatePickerChange(date) { if (passProps.disabled) { return; } var newValue = dateAdapter.format(date); conditionalSetInputValue(newValue); if (propOnChange) { propOnChange({ value: newValue, date: date }); } }; return /*#__PURE__*/React.createElement(HtmlDiv, { className: classnames(baseClassName, className, (_a = {}, _a[dateInputClassNames.disabled] = !!passProps.disabled, _a[dateInputClassNames.error] = status === 'error', _a[dateInputClassNames.success] = status === 'success', _a[dateInputClassNames.fullWidth] = fullWidth, _a[dateInputClassNames.hasPicker] = datePickerEnabled, _a)), style: style }, /*#__PURE__*/React.createElement(HtmlDiv, { className: dateInputClassNames.styleWrapper }, /*#__PURE__*/React.createElement(Label, { htmlFor: id, labelMode: labelMode, optionalText: optionalText, className: classnames((_b = {}, _b[dateInputClassNames.labelIsVisible] = labelMode !== 'hidden', _b)), tooltipComponent: tooltipComponent }, labelText), /*#__PURE__*/React.createElement(HintText, { id: hintTextId }, hintText), /*#__PURE__*/React.createElement(HtmlDiv, { className: dateInputClassNames.inputAndPickerWrapper }, /*#__PURE__*/React.createElement(HtmlDiv, { className: dateInputClassNames.inputElementContainer }, /*#__PURE__*/React.createElement(Debounce, { waitFor: debounce }, function (debouncer) { return /*#__PURE__*/React.createElement(HtmlInput, __assign({ autoComplete: "off" }, passProps, { value: 'value' in props ? value : inputValue, id: id, className: dateInputClassNames.inputElement, forwardedRef: forkRefs(inputRef, definedRef), placeholder: visualPlaceholder, "aria-invalid": status === 'error' ? true : undefined }, getConditionalAriaProp('aria-describedby', [statusText ? statusTextId : undefined, hintText ? hintTextId : undefined, ariaDescribedBy]), { onChange: function onChange(event) { var eventValue = event.currentTarget.value; conditionalSetInputValue(eventValue); if (propOnChange) { debouncer(propOnChange, { value: eventValue, date: dateAdapter.parse(eventValue) }); } } })); })), datePickerEnabled && ( /*#__PURE__*/React.createElement(HtmlDiv, { className: dateInputClassNames.pickerElementContainer }, /*#__PURE__*/React.createElement(HtmlButton, { forwardedRef: openButtonRef, className: classnames(dateInputClassNames.pickerButton, (_c = {}, _c[dateInputClassNames.pickerButtonDisabled] = passProps.disabled, _c)), onClick: function onClick() { return toggleCalendar(!calendarVisible); }, disabled: passProps.disabled, onBlur: function onBlur(event) { if (!!onDatePickerButtonBlur) { onDatePickerButtonBlur(event); } } }, /*#__PURE__*/React.createElement(VisuallyHidden, null, buttonDateLabel || texts.openButtonLabel), /*#__PURE__*/React.createElement(IconCalendar, { className: dateInputClassNames.pickerIcon, "aria-hidden": true })), /*#__PURE__*/React.createElement(DatePicker, { openButtonRef: openButtonRef, isOpen: calendarVisible, onClose: function onClose(focus) { return toggleCalendar(false, focus); }, onChange: function onChange(eventValue) { return onDatePickerChange(eventValue); }, shouldDisableDate: shouldDisableDate, initialDate: initialDate, inputValue: inputValueAsDate, texts: texts, minDate: effectiveMinDate, maxDate: effectiveMaxDate, smallScreen: smallScreen, userProps: customDatePickerProps, position: datePickerPosition })))), /*#__PURE__*/React.createElement(StatusText, { id: statusTextId, className: classnames((_d = {}, _d[dateInputClassNames.statusTextHasContent] = !!statusText, _d)), status: status, ariaLiveMode: statusTextAriaLiveMode, disabled: passProps.disabled }, statusText))); }; var StyledDateInput = styled(function (_a) { _a.theme; _a.globalMargins; var passProps = __rest(_a, ["theme", "globalMargins"]); return /*#__PURE__*/React.createElement(BaseDateInput, __assign({}, passProps)); }).withConfig({ componentId: "sc-1ix9bm-0" })(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n ", "\n"], ["\n ", "\n"])), function (_a) { var theme = _a.theme, globalMargins = _a.globalMargins, rest = __rest(_a, ["theme", "globalMargins"]); var _b = separateMarginProps(rest), marginProps = _b[0]; var cleanedGlobalMargins = filterDuplicateKeys(globalMargins.dateInput, marginProps); return baseStyles(theme, cleanedGlobalMargins, marginProps); }); var DateInput = /*#__PURE__*/forwardRef(function (props, ref) { var propId = props.id, passProps = __rest(props, ["id"]); return /*#__PURE__*/React.createElement(SpacingConsumer, null, function (_a) { var margins = _a.margins; return /*#__PURE__*/React.createElement(SuomifiThemeConsumer, null, function (_a) { var suomifiTheme = _a.suomifiTheme; return /*#__PURE__*/React.createElement(AutoId, { id: propId }, function (id) { return /*#__PURE__*/React.createElement(StyledDateInput, __assign({ theme: suomifiTheme, id: id, forwardedRef: ref, globalMargins: margins }, passProps)); }); }); }); }); DateInput.displayName = 'DateInput'; var templateObject_1; export { DateInput, dateInputClassNames }; //# sourceMappingURL=DateInput.js.map