UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

449 lines 16.9 kB
import _get from "lodash/get"; var __rest = this && this.__rest || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; /* eslint-disable jsx-a11y/click-events-have-key-events */ /* eslint-disable jsx-a11y/no-static-element-interactions */ import React from 'react'; import cls from 'classnames'; import PropTypes from 'prop-types'; import DateInputFoundation from '@douyinfe/semi-foundation/lib/es/datePicker/inputFoundation'; import { cssClasses, strings } from '@douyinfe/semi-foundation/lib/es/datePicker/constants'; import { noop } from '@douyinfe/semi-foundation/lib/es/utils/function'; import isNullOrUndefined from '@douyinfe/semi-foundation/lib/es/utils/isNullOrUndefined'; import { IconCalendar, IconCalendarClock, IconClear } from '@douyinfe/semi-icons'; import BaseComponent from '../_base/baseComponent'; import Input from '../input/index'; import { InsetDateInput, InsetTimeInput } from './insetInput'; // eslint-disable-next-line @typescript-eslint/ban-types export default class DateInput extends BaseComponent { constructor(props) { super(props); this.handleChange = (value, e) => this.foundation.handleChange(value, e); this.handleEnterPress = e => this.foundation.handleInputComplete(e); this.handleInputClear = e => this.foundation.handleInputClear(e); this.handleRangeInputChange = (rangeStart, rangeEnd, e) => { const rangeInputValue = this.getRangeInputValue(rangeStart, rangeEnd); this.foundation.handleChange(rangeInputValue, e); }; this.handleRangeInputClear = e => { this.foundation.handleRangeInputClear(e); }; this.handleRangeInputEnterPress = (e, rangeStart, rangeEnd) => { const rangeInputValue = this.getRangeInputValue(rangeStart, rangeEnd); this.foundation.handleRangeInputEnterPress(e, rangeInputValue); }; this.handleRangeInputEndKeyPress = e => { this.foundation.handleRangeInputEndKeyPress(e); }; this.handleRangeInputFocus = (e, rangeType) => { this.foundation.handleRangeInputFocus(e, rangeType); }; this.handleRangeStartFocus = e => { this.handleRangeInputFocus(e, 'rangeStart'); }; this.handleInsetInputChange = options => { this.foundation.handleInsetInputChange(options); }; this.getRangeInputValue = (rangeStart, rangeEnd) => { const { rangeSeparator } = this.props; const rangeInputValue = `${rangeStart}${rangeSeparator}${rangeEnd}`; return rangeInputValue; }; this.foundation = new DateInputFoundation(this.adapter); } get adapter() { var _this = this; return Object.assign(Object.assign({}, super.adapter), { updateIsFocusing: isFocusing => this.setState({ isFocusing }), notifyClick: function () { return _this.props.onClick(...arguments); }, notifyChange: function () { return _this.props.onChange(...arguments); }, notifyEnter: function () { return _this.props.onEnterPress(...arguments); }, notifyBlur: function () { return _this.props.onBlur(...arguments); }, notifyClear: function () { return _this.props.onClear(...arguments); }, notifyFocus: function () { return _this.props.onFocus(...arguments); }, notifyRangeInputClear: function () { return _this.props.onRangeClear(...arguments); }, notifyRangeInputFocus: function () { return _this.props.onFocus(...arguments); }, notifyTabPress: function () { return _this.props.onRangeEndTabPress(...arguments); }, notifyInsetInputChange: options => this.props.onInsetInputChange(options) }); } componentDidMount() { this.foundation.init(); } componentWillUnmount() { this.foundation.destroy(); } formatText(value) { return value && value.length ? this.foundation.formatShowText(value) : ''; } renderRangePrefix() { const { prefix, insetLabel, prefixCls, disabled, rangeInputFocus } = this.props; const labelNode = prefix || insetLabel; return labelNode ? (/*#__PURE__*/React.createElement("div", { className: `${prefixCls}-range-input-prefix`, onClick: e => !disabled && !rangeInputFocus && this.handleRangeStartFocus(e), "x-semi-prop": "prefix,insetLabel" }, labelNode)) : null; } renderRangeSeparator(rangeStart, rangeEnd) { const { disabled, rangeSeparator } = this.props; const separatorCls = cls({ [`${cssClasses.PREFIX}-range-input-separator`]: true, [`${cssClasses.PREFIX}-range-input-separator-active`]: (rangeStart || rangeEnd) && !disabled }); return /*#__PURE__*/React.createElement("span", { onClick: e => !disabled && this.handleRangeStartFocus(e), className: separatorCls }, rangeSeparator); } renderRangeClearBtn(rangeStart, rangeEnd) { const { showClear, prefixCls, disabled, clearIcon, showClearIgnoreDisabled } = this.props; const isRealDisabled = disabled && !showClearIgnoreDisabled; const allowClear = (rangeStart || rangeEnd) && showClear && !isRealDisabled; return allowClear ? (/*#__PURE__*/React.createElement("div", { role: "button", tabIndex: 0, "aria-label": "Clear range input value", className: `${prefixCls}-range-input-clearbtn`, onMouseDown: e => this.handleRangeInputClear(e) }, clearIcon ? clearIcon : /*#__PURE__*/React.createElement(IconClear, { "aria-hidden": true }))) : null; } renderRangeSuffix(suffix) { const { prefixCls, disabled, rangeInputFocus } = this.props; const rangeSuffix = suffix ? (/*#__PURE__*/React.createElement("div", { className: `${prefixCls}-range-input-suffix`, onClick: e => !disabled && !rangeInputFocus && this.handleRangeStartFocus(e) }, suffix)) : null; return rangeSuffix; } renderRangeInput(rangeProps) { const { // this.props placeholder, inputStyle, disabled, inputReadOnly, autofocus, size, // compute props text, suffix, inputCls, // range only props rangeInputStartRef, rangeInputEndRef, rangeInputFocus, prefixCls, rangeSeparator, borderless } = rangeProps; const [rangeStart, rangeEnd = ''] = text.split(rangeSeparator) || []; const rangeSize = size === 'large' ? 'default' : 'small'; const rangePlaceholder = Array.isArray(placeholder) ? placeholder : [placeholder, placeholder]; const [rangeStartPlaceholder, rangeEndPlaceholder] = rangePlaceholder; const inputLeftWrapperCls = cls(`${prefixCls}-range-input-wrapper-start`, `${prefixCls}-range-input-wrapper`, { [`${prefixCls}-range-input-wrapper-active`]: rangeInputFocus === 'rangeStart' && !disabled, [`${prefixCls}-range-input-wrapper-start-with-prefix`]: this.props.prefix || this.props.insetLabel, [`${prefixCls}-borderless`]: borderless }); const inputRightWrapperCls = cls(`${prefixCls}-range-input-wrapper-end`, `${prefixCls}-range-input-wrapper`, { [`${prefixCls}-range-input-wrapper-active`]: rangeInputFocus === 'rangeEnd' && !disabled, [`${prefixCls}-borderless`]: borderless }); return /*#__PURE__*/React.createElement(React.Fragment, null, this.renderRangePrefix(), /*#__PURE__*/React.createElement("div", { onClick: e => !disabled && this.handleRangeInputFocus(e, 'rangeStart'), className: `${inputCls} ${inputLeftWrapperCls}` }, /*#__PURE__*/React.createElement(Input, { borderless: borderless, size: rangeSize, style: inputStyle, disabled: disabled, readonly: inputReadOnly, placeholder: rangeStartPlaceholder, value: rangeStart, // range input onBlur function is called when panel is closed // onBlur={noop} onChange: (rangeStartValue, e) => this.handleRangeInputChange(rangeStartValue, rangeEnd, e), onEnterPress: e => this.handleRangeInputEnterPress(e, rangeStart, rangeEnd), onFocus: e => this.handleRangeInputFocus(e, 'rangeStart'), autoFocus: autofocus, ref: rangeInputStartRef })), this.renderRangeSeparator(rangeStart, rangeEnd), /*#__PURE__*/React.createElement("div", { className: `${inputCls} ${inputRightWrapperCls}`, onClick: e => !disabled && this.handleRangeInputFocus(e, 'rangeEnd') }, /*#__PURE__*/React.createElement(Input, { borderless: borderless, size: rangeSize, style: inputStyle, disabled: disabled, readonly: inputReadOnly, placeholder: rangeEndPlaceholder, value: rangeEnd, // range input onBlur function is called when panel is closed // onBlur={noop} onChange: (rangeEndValue, e) => this.handleRangeInputChange(rangeStart, rangeEndValue, e), onEnterPress: e => this.handleRangeInputEnterPress(e, rangeStart, rangeEnd), onFocus: e => this.handleRangeInputFocus(e, 'rangeEnd'), onKeyDown: this.handleRangeInputEndKeyPress, ref: rangeInputEndRef })), this.renderRangeClearBtn(rangeStart, rangeEnd), this.renderRangeSuffix(suffix)); } isRenderMultipleInputs() { const { type } = this.props; // isRange and not monthRange render multiple inputs return type.includes('Range') && type !== 'monthRange'; } renderInputInset() { const { type, handleInsetDateFocus, handleInsetTimeFocus, value, insetInputValue, prefixCls, rangeInputStartRef, rangeInputEndRef, density, insetInput } = this.props; const newInsetInputValue = this.foundation.getInsetInputValue({ value, insetInputValue }); const { dateStart, dateEnd, timeStart, timeEnd } = _get(insetInput, 'placeholder', {}); const { datePlaceholder, timePlaceholder } = this.foundation.getInsetInputPlaceholder(); const insetInputWrapperCls = `${prefixCls}-inset-input-wrapper`; const separatorCls = `${prefixCls}-inset-input-separator`; return /*#__PURE__*/React.createElement("div", { className: insetInputWrapperCls, "x-type": type }, /*#__PURE__*/React.createElement(InsetDateInput, { forwardRef: rangeInputStartRef, insetInputValue: newInsetInputValue, placeholder: dateStart !== null && dateStart !== void 0 ? dateStart : datePlaceholder, valuePath: 'monthLeft.dateInput', onChange: this.handleInsetInputChange, onFocus: e => handleInsetDateFocus(e, 'rangeStart') }), /*#__PURE__*/React.createElement(InsetTimeInput, { disabled: !newInsetInputValue.monthLeft.dateInput, insetInputValue: newInsetInputValue, placeholder: timeStart !== null && timeStart !== void 0 ? timeStart : timePlaceholder, type: type, valuePath: 'monthLeft.timeInput', onChange: this.handleInsetInputChange, onFocus: handleInsetTimeFocus }), this.isRenderMultipleInputs() && (/*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", { className: separatorCls }, density === 'compact' ? null : '-'), /*#__PURE__*/React.createElement(InsetDateInput, { forwardRef: rangeInputEndRef, insetInputValue: newInsetInputValue, placeholder: dateEnd !== null && dateEnd !== void 0 ? dateEnd : datePlaceholder, valuePath: 'monthRight.dateInput', onChange: this.handleInsetInputChange, onFocus: e => handleInsetDateFocus(e, 'rangeEnd') }), /*#__PURE__*/React.createElement(InsetTimeInput, { disabled: !newInsetInputValue.monthRight.dateInput, insetInputValue: newInsetInputValue, placeholder: timeEnd !== null && timeEnd !== void 0 ? timeEnd : timePlaceholder, type: type, valuePath: 'monthRight.timeInput', onChange: this.handleInsetInputChange, onFocus: handleInsetTimeFocus })))); } renderTriggerInput() { const _a = this.props, { placeholder, type, value, inputValue, inputStyle, disabled, showClear, inputReadOnly, insetLabel, validateStatus, block, prefixCls, multiple, // Whether to allow multiple values for email and file types dateFnsLocale, // No need to pass to input onBlur, onClear, onFocus, prefix, autofocus, size, inputRef, // range input support props, no need passing to not range type rangeInputStartRef, rangeInputEndRef, onRangeClear, onRangeBlur, onRangeEndTabPress, rangeInputFocus, rangeSeparator, insetInput, insetInputValue, defaultPickerValue, showClearIgnoreDisabled } = _a, rest = __rest(_a, ["placeholder", "type", "value", "inputValue", "inputStyle", "disabled", "showClear", "inputReadOnly", "insetLabel", "validateStatus", "block", "prefixCls", "multiple", "dateFnsLocale", "onBlur", "onClear", "onFocus", "prefix", "autofocus", "size", "inputRef", "rangeInputStartRef", "rangeInputEndRef", "onRangeClear", "onRangeBlur", "onRangeEndTabPress", "rangeInputFocus", "rangeSeparator", "insetInput", "insetInputValue", "defaultPickerValue", "showClearIgnoreDisabled"]); const dateIcon = /*#__PURE__*/React.createElement(IconCalendar, { "aria-hidden": true }); const dateTimeIcon = /*#__PURE__*/React.createElement(IconCalendarClock, { "aria-hidden": true }); const suffix = type.includes('Time') ? dateTimeIcon : dateIcon; let text = ''; if (!isNullOrUndefined(inputValue)) { text = inputValue; } else if (value) { text = this.formatText(value); } const inputCls = cls({ [`${prefixCls}-input-readonly`]: inputReadOnly, [`${prefixCls}-monthRange-input`]: type === 'monthRange' }); const rangeProps = Object.assign(Object.assign({}, this.props), { text, suffix, inputCls }); return this.isRenderMultipleInputs() ? this.renderRangeInput(rangeProps) : (/*#__PURE__*/React.createElement(Input, Object.assign({}, rest, { ref: inputRef, insetLabel: insetLabel, disabled: disabled, showClearIgnoreDisabled: showClearIgnoreDisabled, readonly: inputReadOnly, className: inputCls, style: inputStyle, hideSuffix: showClear, placeholder: type === 'monthRange' && Array.isArray(placeholder) ? placeholder[0] + rangeSeparator + placeholder[1] : placeholder, onEnterPress: this.handleEnterPress, onChange: this.handleChange, onClear: this.handleInputClear, suffix: suffix, showClear: showClear, value: text, validateStatus: validateStatus, prefix: prefix, autoFocus: autofocus, size: size, onBlur: onBlur, onFocus: onFocus }))); } render() { const { insetInput } = this.props; return insetInput ? this.renderInputInset() : this.renderTriggerInput(); } } DateInput.propTypes = { borderless: PropTypes.bool, onClick: PropTypes.func, onChange: PropTypes.func, onEnterPress: PropTypes.func, onBlur: PropTypes.func, onClear: PropTypes.func, onFocus: PropTypes.func, value: PropTypes.array, disabled: PropTypes.bool, type: PropTypes.oneOf(strings.TYPE_SET), showClear: PropTypes.bool, format: PropTypes.string, inputStyle: PropTypes.object, inputReadOnly: PropTypes.bool, insetLabel: PropTypes.node, validateStatus: PropTypes.string, prefix: PropTypes.node, prefixCls: PropTypes.string, dateFnsLocale: PropTypes.object.isRequired, placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.array]), rangeInputFocus: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), rangeInputStartRef: PropTypes.object, rangeInputEndRef: PropTypes.object, rangeSeparator: PropTypes.string, insetInput: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), insetInputValue: PropTypes.object, defaultPickerValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object, PropTypes.array]) }; DateInput.defaultProps = { borderless: false, showClear: true, onClick: noop, onChange: noop, onEnterPress: noop, onBlur: noop, onClear: noop, onFocus: noop, type: 'date', inputStyle: {}, inputReadOnly: false, prefixCls: cssClasses.PREFIX, rangeSeparator: strings.DEFAULT_SEPARATOR_RANGE };