@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
JavaScript
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
};