Skip to content

Commit

Permalink
feat(DatePicker2): support outputFormat
Browse files Browse the repository at this point in the history
  • Loading branch information
皆虚 authored and youluna committed Jun 21, 2021
1 parent d011a72 commit 976d712
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 49 deletions.
3 changes: 2 additions & 1 deletion docs/date-picker2/index.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ DatePicker are used to select a single date for an input.
| defaultVisibleMonth | Default visible month <br><br>**signature**:<br>Function() => MomentObject<br>**return**:<br>{MomentObject} moment instance with specified month<br> | Function | - |
| value | Range value `[moment, moment]` | Array | - |
| defaultValue | Default range value `[moment, moment]` | Array | - |
| format | Date format | String | 'YYYY-MM-DD' |
| format | Date format | String | Function | 'YYYY-MM-DD' |
| outputFormat | Date output format | String | ((date: Dayjs, dateStr: String) => any) | |
| placeholder | input hints:`['start date', 'end date']` | Array | - |
| showTime | Enable time picker | Object/Boolean | false |
| resetTime | If reset time for every select | Boolean | false |
Expand Down
2 changes: 2 additions & 0 deletions docs/date-picker2/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ ReactDOM.render(<App />, mountNode);
| value | 日期值(受控) | Dayjs/String | - |
| defaultValue | 初始日期值 | Dayjs/String | - |
| format | 日期格式 | String | `YYYY-MM` |
| outputFormat | 输出格式 | String | ((date: Dayjs, dateStr: String) => any) | |
| onChange | 日期值改变时的回调<br><br>**签名**:<br>Function(value: Dayjs/String) => void<br>**参数**:<br>_value_: {Dayjs/String} 日期值 | Function | func.noop |
| onOk | 点击确认按钮时的回调<br><br>**签名**:<br>Function() => Array<br>**返回值**:<br>{Array} 日期范围<br> | Function | func.noop

Expand All @@ -96,5 +97,6 @@ ReactDOM.render(<App />, mountNode);
| value | 日期值(受控) | [Dayjs, Dayjs] | - |
| defaultValue | 初始日期值 | [Dayjs, Dayjs] | - |
| format | 日期格式 | String/Function | `YYYY-MM` |
| outputFormat | 输出格式 | String | ((date: [Dayjs, Dayjs], dateStr: [String, String]) => any) | |
| onChange | 日期值改变时的回调<br><br>**签名**:<br>Function(value) => void<br>**参数**:<br>_value_: {[Dayjs, Dayjs]} 日期范围 | Function | func.noop |
| onOk | 点击确认按钮时的回调<br><br>**签名**:<br>Function(value) => void<br>**参数**:<br>_value_: {[Dayjs, Dayjs]} 日期范围<br> | Function | func.noop
150 changes: 103 additions & 47 deletions src/date-picker2/picker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,25 @@ const { Popup } = Overlay;
const { renderNode } = func;
const { pickProps, pickOthers } = obj;

/**
* 判断值是否改变
* @param {dayjs.ConfigType}} newValue
* @param {dayjs.ConfigType} oldValue
* @returns {boolean}
*/
function isValueChanged(newValue, oldValue) {
return Array.isArray(newValue)
? isValueChanged(newValue[0], oldValue && oldValue[0]) || isValueChanged(newValue[1], oldValue && oldValue[1])
: newValue !== oldValue && !datejs(newValue).isSame(oldValue);
}

// 返回日期字符串
function getInputValue(value, fmt) {
/**
* 获取输入框值
* @param {*} value 日期值
* @param {string | funtion} fmt 日期格式
* @returns {string | string[]}
*/
function fmtValue(value, fmt) {
const formater = (v, idx) => {
if (Array.isArray(fmt)) {
fmt = fmt[idx];
Expand All @@ -39,9 +50,8 @@ function getInputValue(value, fmt) {

/**
* 日期检验:无效值返回 null
*
* @param {Dayjs.ConfigType} value
* @returns Dayjs | null
* @param {dayjs.ConfigType} value
* @returns {Dayjs | null}
*/
function checkDate(value) {
/**
Expand All @@ -65,7 +75,7 @@ function checkDate(value) {
* @returns {Dayjs[] | null[]}
*/
function checkRangeDate(value, inputType, disabled, strictly = true) {
const [begin, end] = Array.isArray(value) ? [0, 1].map((i) => checkDate(value[i])) : [null, null];
const [begin, end] = Array.isArray(value) ? [0, 1].map(i => checkDate(value[i])) : [null, null];
const [disabledBegin, disabledEnd] = Array.isArray(disabled) ? disabled : [disabled, disabled];

/**
Expand Down Expand Up @@ -133,9 +143,20 @@ class Picker extends React.Component {
placeholder: SharedPT.placeholder,
disabled: SharedPT.disabled,
inputReadOnly: SharedPT.readOnly,
/**
* 日期显示格式
*/
format: SharedPT.format,
label: PT.node,
separator: PT.node,
/**
* 输出格式:控制 onChange、onOk 事件的输出值格式
* - string 类型:根据时间格式进行转换
* - function 类型:((date: Dayjs, dateStr: string) => any)
*
* @version 1.23.7
*/
outputFormat: SharedPT.format,

// popup
followTrigger: PT.bool,
Expand Down Expand Up @@ -189,7 +210,7 @@ class Picker extends React.Component {

this.state = {
value,
inputValue: getInputValue(value, format),
inputValue: fmtValue(value, format),
curValue: value, // 当前状态值
...this.state,
};
Expand All @@ -207,17 +228,18 @@ class Picker extends React.Component {
};

/**
* 当前输入框可能被 disabled
* 如果另一个输入框非 disabled 则切换到另一个输入框
* 如果当前输入框可能被 disabled
* 且另一个输入框非 disabled 则切换到另一个输入框
*/
if (isRange) {
let { inputType } = state;

const _disabled = Array.isArray(disabled) ? disabled : [disabled, disabled];

let { inputType } = state;
if (_disabled[inputType]) {
const otherType = switchInputType(state.inputType);

if (_disabled[otherType]) {
if (!_disabled[otherType]) {
inputType = otherType;
}
}
Expand All @@ -233,7 +255,7 @@ class Picker extends React.Component {
...newState,
value,
curValue: value,
inputValue: getInputValue(value, format),
inputValue: fmtValue(value, format),
};
}
}
Expand All @@ -242,9 +264,12 @@ class Picker extends React.Component {
}

componentWillUnmount() {
[this.clearTimeoutId, this.timeoutId].forEach((id) => id && clearTimeout(id));
[this.clearTimeoutId, this.timeoutId].forEach(id => id && clearTimeout(id));
}

/**
* 获取初始值
*/
getInitValue = () => {
const { props } = this;
const { type, value, defaultValue } = props;
Expand All @@ -255,18 +280,31 @@ class Picker extends React.Component {

return this.checkValue(val);
};

/**
* 获取 RangePicker 输入框初始输入状态
* @returns {Object} inputState
* @returns {boolean} inputState.justBeginInput 是否初始输入
* @returns {number} inputState.inputType 当前输入框
*/
getInitRangeInputState = () => {
return {
justBeginInput: this.isEnabled(),
inputType: this.isEnabled(0) ? DATE_INPUT_TYPE.BEGIN : DATE_INPUT_TYPE.END,
};
};

// 判断弹层是否显示
/**
* 处理点击文档区域导致的弹层收起逻辑
* @param {boolean} visible 是否可见
* @param {string} type 事件类型
*/
handleVisibleChange = (visible, type) => {
if (type === 'docClick') {
visible || this.handleChange(this.state.curValue, 'VISIBLE_CHANGE');

// 弹层收起 触发 Change 逻辑
if (!visible) {
this.handleChange(this.state.curValue, 'VISIBLE_CHANGE');
}
this.onVisibleChange(visible);
}
};
Expand All @@ -277,7 +315,7 @@ class Picker extends React.Component {
: checkDate(value);
};

handleInputFocus = (inputType) => {
handleInputFocus = inputType => {
let inputEl = this.dateInput && this.dateInput.input;

if (this.state.isRange) {
Expand All @@ -287,7 +325,7 @@ class Picker extends React.Component {
inputEl && inputEl.focus();
};

handleMouseDown = (e) => {
handleMouseDown = e => {
e.preventDefault();
};

Expand Down Expand Up @@ -334,8 +372,10 @@ class Picker extends React.Component {
};

handleClear = () => {
// 清空输入之后 input组件内部会让第二个输入框获得焦点
// 所以这里需要设置setTimeout才能让第一个input获得焦点
/**
* 清空输入之后 input 组件内部会让第二个输入框获得焦点
* 所以这里需要设置 setTimeout 才能让第一个 input 获得焦点
*/
this.clearTimeoutId = setTimeout(() => {
this.handleInputFocus(0);
});
Expand All @@ -346,7 +386,7 @@ class Picker extends React.Component {
});
};

shouldSwitchInput = (value) => {
shouldSwitchInput = value => {
const { inputType, justBeginInput } = this.state;
const idx = justBeginInput ? switchInputType(inputType) : value.indexOf(null);

Expand All @@ -359,7 +399,7 @@ class Picker extends React.Component {
return false;
};

isEnabled = (idx) => {
isEnabled = idx => {
const { disabled } = this.props;

return Array.isArray(disabled)
Expand All @@ -369,13 +409,28 @@ class Picker extends React.Component {
: !disabled;
};

onKeyDown = e => {
switch (e.keyCode) {
case KEYCODE.ENTER: {
const { inputValue } = this.state;
this.onClick();
this.handleChange(inputValue, 'KEYDOWN_ENTER');
break;
}
default:
return;
}
};

handleChange = (v, eventType) => {
const { format } = this.props;
const { isRange, showOk, value } = this.state;
const forceEvents = ['KEYDOWN_ENTER', 'CLICK_OK', 'CLICK_PRESET', 'INPUT_CLEAR'];

// 在显示确认按键且关闭弹层的时候 将当前值设置回确认值
// 在有确认按键的时候 需要点击确认值才生效
/**
* 在显示确认按键且关闭弹层的时候 将当前值设置回确认值
* 在有确认按键的时候 需要点击确认值才生效
*/
if (showOk && eventType === 'VISIBLE_CHANGE') {
v = value;
} else {
Expand All @@ -385,7 +440,7 @@ class Picker extends React.Component {

this.setState({
curValue: v,
inputValue: getInputValue(v, format),
inputValue: fmtValue(v, format),
});

if (!showOk || forceEvents.includes(eventType)) {
Expand All @@ -400,20 +455,7 @@ class Picker extends React.Component {
}
};

onKeyDown = (e) => {
switch (e.keyCode) {
case KEYCODE.ENTER: {
const { inputValue } = this.state;
this.onClick();
this.handleChange(inputValue, 'KEYDOWN_ENTER');
break;
}
default:
return;
}
};

onChange = (v) => {
onChange = v => {
const { value } = this.state;
const { format } = this.props;

Expand All @@ -422,35 +464,34 @@ class Picker extends React.Component {
if ('value' in this.props) {
this.setState({
curValue: value,
inputValue: getInputValue(value, format),
inputValue: fmtValue(value, format),
});
} else {
v = this.checkValue(v);

this.setState({
value: v,
curValue: v,
inputValue: getInputValue(v, format),
inputValue: fmtValue(v, format),
});
}

func.invoke(this.props, 'onChange', [v, getInputValue(v, format)]);
func.invoke(this.props, 'onChange', this.getOutputArgs(v));
}

this.onVisibleChange(false);
};

onOk = () => {
const { inputValue } = this.state;
const { format } = this.props;
const checkedValue = this.checkValue(inputValue);

const result = func.invoke(this.props, 'onOk', [checkedValue, getInputValue(checkedValue, format)]);
const result = func.invoke(this.props, 'onOk', this.getOutputArgs(checkedValue));

result !== false && this.handleChange(inputValue, 'CLICK_OK');
};

onInputTypeChange = (idx) => {
onInputTypeChange = idx => {
const { inputType, visible } = this.state;

if (idx !== inputType) {
Expand All @@ -470,6 +511,21 @@ class Picker extends React.Component {
}
};

/**
* 获取 `onChange` 和 `onOk` 方法的输出参数
* @param {Dayjs} value
* @returns 默认返回 `Dayjs` 实例和 `format` 格式化的值
* 如果传了了 `outputFormat` 属性则返回 `outputFormat` 格式化的值
*/
getOutputArgs = value => {
const { outputFormat, format } = this.props;

if (outputFormat) {
return [fmtValue(value, outputFormat)];
}
return [value, fmtValue(value, format)];
};

getCurrentAlign = ({ align }) => {
this.setState({
align,
Expand Down Expand Up @@ -540,7 +596,7 @@ class Picker extends React.Component {
}

const visible = 'visible' in this.props ? this.props.visible : this.state.visible;
const allDisabled = isRange && Array.isArray(disabled) ? disabled.every((v) => v) : disabled;
const allDisabled = isRange && Array.isArray(disabled) ? disabled.every(v => v) : disabled;
const sharedProps = {
rtl,
prefix,
Expand All @@ -565,7 +621,7 @@ class Picker extends React.Component {
onInput: handleInput,
readOnly: inputReadOnly,
inputProps: this.props.inputProps,
ref: (el) => (this.dateInput = el),
ref: el => (this.dateInput = el),
};

// 禁用状态下 不允许清空
Expand Down
Loading

0 comments on commit 976d712

Please sign in to comment.