wuji.xwt 5d259055fb refactor: code style
Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/3681622

* chore: remove unnecessary code

* refactor: react-render using TypeScript

* chore: build-script

* refactor: editor-skeleton

* refactor: designer

* refactor: material-parser

* refactor: editor-setters

* refactor: js to ts for rax-provider 

Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/3678180

* refactor: rax-provider

* feat: add build command

* chore: compilerOptions for rax-provider

* refactor: JS to TS for Rax Renderer 

Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/3678935

* refactor: rax-renderer

* Merge remote-tracking branch 'origin/refactor/js-to-ts' into refactor/js2ts-rax-renderer

* Merge remote-tracking branch 'origin/refactor/js-to-ts' into refactor/js2ts-rax-renderer

* refactor: ts-nocheck

* chore: ts compile error

* fix: ts rootDir

* fix: compile error

* chore: using same tsconfig for rax component

* refactor: ts compile rax-renderer && rax-provider

* Merge remote-tracking branch 'origin/release/1.0.0' into refactor/js-to-ts

# Conflicts:
#	packages/rax-render/src/utils/appHelper.js
#	packages/rax-render/src/utils/appHelper.ts
#	packages/utils/src/appHelper.ts

* refactor: no JS file

* refactor: remove lint

* feat: add xima

* feat: add eslint ignore

* style: fix by lint

* feat: lint command

* fix: using the same eslint config

* style: eslint settings

* Merge remote-tracking branch 'origin/release/1.0.0' into refactor/code-style

# Conflicts:
#	packages/plugin-event-bind-dialog/src/index.tsx
#	packages/plugin-source-editor/src/index.tsx
#	packages/runtime/src/core/container.ts
#	packages/runtime/src/core/provider.ts

* Merge remote-tracking branch 'origin/release/1.0.0' into refactor/code-style

# Conflicts:
#	packages/designer/src/document/document-model.ts
#	packages/designer/src/document/node/node-children.ts
#	packages/designer/src/document/node/props/prop.ts
#	packages/plugin-source-editor/src/index.tsx

* fix: 修改dataSource items -> list

* Merge remote-tracking branch 'origin/relase/1.0.0' into refactor/code-style

# Conflicts:
#	packages/react-renderer/package.json

* refactor: component-panel 

plugin-component-pane 代码规范化

Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/3703771

* feat: support bizcomps

* refactor: component-panel

* style: eslint

* Merge branch 'refactor/code-style' into fix/ducheng-source-style

* style: code style

* Merge branch 'fix/ducheng-source-style' into 'refactor/code-style'
Code review title: Fix/ducheng source style
Code review Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/3705972

* style: for demo

* style: for demo-server

* style: for plugin-event-bind-dialog

* style: for plugin-sample-preview

* style: for plugin-undo-redo

* style: plugin-variable-bind-dialog

* style: types

* style: for utils

* Merge remote-tracking branch 'origin/release/1.0.0' into refactor/code-style

# Conflicts:
#	packages/editor-setters/src/expression-setter/locale/snippets.ts
#	packages/editor-setters/src/json-setter/locale/snippets.ts
#	packages/editor-setters/src/locale/snippets.ts
#	packages/plugin-components-pane/package.json
#	packages/rax-render/src/hoc/compWrapper.tsx
#	packages/rax-render/src/utils/index.ts
#	packages/react-renderer/src/context/appContext.ts

* style: editor-preset-general 

editor-preset-general 代码规范化

Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/3707974

* style: editor-preset-general

* fix: should set field

* fix: should set field - demo-server

* refactor(style): fix code style for designer

refactor(style): fix code style for editor-core

refactor(style): fix code style for editor-skeleton

refactor(style): fix code style for react-simulator-renderer

refactor(style): fix code style for rax-simulator-renderer

* Merge branch 'refactor/lihao-code-style' into 'refactor/code-style'
Code review title: refactor(style): fix code style for designer
Code review description: refactor(style): fix code style for editor-core

refactor(style): fix code style for editor-skeleton

refactor(style): fix code style for react-simulator-renderer

refactor(style): fix code style for rax-simulator-renderer
Code review Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/3709472

* style: react/no-multi-comp set to 0 for designer

* style: ignore editor-prset-vision

* style: fix for plugin-outline-pane

* style: fix for rax-provider

* style: react-provider

* style: runtime

* style: rax-render

* Merge remote-tracking branch 'origin/release/1.0.0' into refactor/code-style

# Conflicts:
#	packages/editor-setters/src/expression-setter/index.tsx
#	packages/plugin-source-editor/src/index.tsx
#	packages/plugin-source-editor/src/transform.ts

* refactor: material parser code style 

1. 修复eslint问题
2. instanceOf => any
3. 修复node类型解析失败问题

Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/3716330

* refactor: material parser code style

* refactor: code-generator code style 

1. rax 出码合并
2. code style 修复

注:合并的代码中带了 datasource 的

Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/3717159

* Merge branch 'feat/rax-code-generator' of gitlab.alibaba-inc.com:ali-lowcode/ali-lowcode-engine into feat/rax-code-generator

# Conflicts:
#	packages/code-generator/src/generator/ProjectBuilder.ts
#	packages/code-generator/src/parser/SchemaParser.ts
#	packages/code-generator/src/plugins/component/rax/jsx.ts
#	packages/code-generator/src/plugins/project/constants.ts
#	packages/code-generator/src/plugins/project/framework/rax/plugins/packageJSON.ts
#	packages/code-generator/src/plugins/project/i18n.ts
#	packages/code-generator/src/publisher/disk/index.ts
#	packages/code-generator/src/publisher/disk/utils.ts
#	packages/code-generator/src/types/core.ts
#	packages/code-generator/src/types/schema.ts
#	packages/code-generator/src/utils/compositeType.ts
#	packages/code-generator/src/utils/nodeToJSX.ts

* refactor: code-generator

* Merge remote-tracking branch 'origin/refactor/code-style' into refactor/code-style-code-generator

# Conflicts:
#	.vscode/launch.json

* Revert "refactor: code-generator code style "

This reverts commit ebc78e8788f83e8fda0e146758af43b878125c10.

* chore: eslintrc && eslintignore

* style: for plugin-source-editor

* style: fix by eslint --fix

* style: scripts/

* style: datasource-engine

* feat: pre-commit

* Merge branch 'refactor/code-style' of gitlab.alibaba-inc.com:ali-lowcode/ali-lowcode-engine into refactor/code-style

# Conflicts:
#	.eslintignore
#	packages/code-generator/src/parser/SchemaParser.ts
#	packages/code-generator/src/plugins/component/rax/containerInitState.ts
#	packages/code-generator/src/plugins/component/rax/containerInjectDataSourceEngine.ts
#	packages/code-generator/src/plugins/component/rax/containerLifeCycle.ts
#	packages/code-generator/src/plugins/project/framework/rax/plugins/buildConfig.ts
#	packages/code-generator/src/utils/OrderedSet.ts
#	packages/code-generator/src/utils/ScopeBindings.ts
#	packages/code-generator/src/utils/expressionParser.ts
#	packages/code-generator/src/utils/schema.ts
#	packages/datasource-engine/src/core/DataSourceEngine.ts
#	packages/datasource-engine/src/core/RuntimeDataSource.ts
#	packages/datasource-engine/src/types/IRuntimeContext.ts
#	packages/datasource-engine/src/types/index.ts

* refactor: code style code-generator 

对 code style 分支上次合并的  code-generator 修改做了 revert

重新在 code style 分支基础上对代码样式做了修复

rax 合并分支会另行 fix 后向 release 分支提 MR

Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/3719702

* refactor: code style fix

* style: for code-generator

* Merge remote-tracking branch 'origin/release/1.0.0' into refactor/code-style

# Conflicts:
#	packages/editor-skeleton/src/transducers/addon-combine.ts
#	packages/plugin-components-pane/package.json
#	packages/plugin-components-pane/src/components/base/index.tsx
#	packages/plugin-components-pane/src/components/component-list/index.tsx
#	packages/plugin-components-pane/src/i18n/index.ts
#	packages/plugin-components-pane/src/i18n/strings/index.ts
#	packages/plugin-components-pane/src/index.tsx
#	packages/plugin-event-bind-dialog/src/index.tsx
#	packages/plugin-source-editor/src/index.tsx
#	packages/plugin-source-editor/src/transform.ts

* style: auto fix
2020-09-15 19:06:35 +08:00

1181 lines
32 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import moment from 'moment';
import ConfigProvider from '../config-provider';
import Overlay from '../overlay';
import Input from '../input';
import Calendar from '../calendar';
import RangeCalendar from '../calendar/range-calendar';
import TimePickerPanel from '../time-picker/panel';
import nextLocale from '../locale/zh-cn';
import { func, obj } from '../util';
import {
PANEL,
resetValueTime,
formatDateValue,
extend,
getDateTimeFormat,
isFunction,
onDateKeydown,
onTimeKeydown,
} from './util';
import PanelFooter from './module/panel-footer';
const { Popup } = Overlay;
function mapInputStateName(name) {
return {
startValue: 'startDateInputStr',
endValue: 'endDateInputStr',
startTime: 'startTimeInputStr',
endTime: 'endTimeInputStr',
}[name];
}
function mapTimeToValue(name) {
return {
startTime: 'startValue',
endTime: 'endValue',
}[name];
}
function getFormatValues(values, format) {
if (!Array.isArray(values)) {
return [null, null];
}
return [
formatDateValue(values[0], format),
formatDateValue(values[1], format),
];
}
/**
* DatePicker.RangePicker
*/
export default class RangePicker extends Component {
static propTypes = {
...ConfigProvider.propTypes,
prefix: PropTypes.string,
rtl: PropTypes.bool,
/**
* 日期范围类型
*/
type: PropTypes.oneOf(['date', 'month', 'year']),
/**
* 默认展示的起始月份
* @return {MomentObject} 返回包含指定月份的 moment 对象实例
*/
defaultVisibleMonth: PropTypes.func,
onVisibleMonthChange: PropTypes.func,
/**
* 日期范围值数组 [moment, moment]
*/
value: PropTypes.array,
/**
* 初始的日期范围值数组 [moment, moment]
*/
defaultValue: PropTypes.array,
/**
* 日期格式
*/
format: PropTypes.string,
/**
* 是否使用时间控件,支持传入 TimePicker 的属性
*/
showTime: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
/**
* 每次选择是否重置时间(仅在 showTime 开启时有效)
*/
resetTime: PropTypes.bool,
/**
* 禁用日期函数
* @param {MomentObject} 日期值
* @param {String} view 当前视图类型year: 年, month: 月, date: 日
* @return {Boolean} 是否禁用
*/
disabledDate: PropTypes.func,
/**
* 自定义面板页脚
* @return {Node} 自定义的面板页脚组件
*/
footerRender: PropTypes.func,
/**
* 日期范围值改变时的回调 [ MomentObject|String, MomentObject|String ]
* @param {Array<MomentObject|String>} value 日期值
*/
onChange: PropTypes.func,
/**
* 点击确认按钮时的回调 返回开始时间和结束时间`[ MomentObject|String, MomentObject|String ]`
* @return {Array} 日期范围
*/
onOk: PropTypes.func,
/**
* 输入框内置标签
*/
label: PropTypes.node,
/**
* 输入框状态
*/
state: PropTypes.oneOf(['error', 'loading', 'success']),
/**
* 输入框尺寸
*/
size: PropTypes.oneOf(['small', 'medium', 'large']),
/**
* 是否禁用
*/
disabled: PropTypes.bool,
/**
* 是否显示清空按钮
*/
hasClear: PropTypes.bool,
/**
* 弹层显示状态
*/
visible: PropTypes.bool,
/**
* 弹层默认是否显示
*/
defaultVisible: PropTypes.bool,
/**
* 弹层展示状态变化时的回调
* @param {Boolean} visible 弹层是否显示
* @param {String} type 触发弹层显示和隐藏的来源 okBtnClick 表示由确认按钮触发; fromTrigger 表示由trigger的点击触发 docClick 表示由document的点击触发
*/
onVisibleChange: PropTypes.func,
/**
* 弹层触发方式
*/
popupTriggerType: PropTypes.oneOf(['click', 'hover']),
/**
* 弹层对齐方式, 具体含义见 OverLay文档
*/
popupAlign: PropTypes.string,
/**
* 弹层容器
* @param {Element} target 目标元素
* @return {Element} 弹层的容器元素
*/
popupContainer: PropTypes.any,
/**
* 弹层自定义样式
*/
popupStyle: PropTypes.object,
/**
* 弹层自定义样式类
*/
popupClassName: PropTypes.string,
/**
* 弹层其他属性
*/
popupProps: PropTypes.object,
/**
* 是否跟随滚动
*/
followTrigger: PropTypes.bool,
/**
* 输入框其他属性
*/
inputProps: PropTypes.object,
/**
* 自定义日期单元格渲染
*/
dateCellRender: PropTypes.func,
/**
* 自定义月份渲染函数
* @param {Object} calendarDate 对应 Calendar 返回的自定义日期对象
* @returns {ReactNode}
*/
monthCellRender: PropTypes.func,
yearCellRender: PropTypes.func, // 兼容 0.x yearCellRender
/**
* 开始日期输入框的 aria-label 属性
*/
startDateInputAriaLabel: PropTypes.string,
/**
* 开始时间输入框的 aria-label 属性
*/
startTimeInputAriaLabel: PropTypes.string,
/**
* 结束日期输入框的 aria-label 属性
*/
endDateInputAriaLabel: PropTypes.string,
/**
* 结束时间输入框的 aria-label 属性
*/
endTimeInputAriaLabel: PropTypes.string,
/**
* 是否为预览态
*/
isPreview: PropTypes.bool,
/**
* 预览态模式下渲染的内容
* @param {Array<MomentObject, MomentObject>} value 日期区间
*/
renderPreview: PropTypes.func,
disableChangeMode: PropTypes.bool,
yearRange: PropTypes.arrayOf(PropTypes.number),
ranges: PropTypes.object, // 兼容0.x版本
locale: PropTypes.object,
className: PropTypes.string,
name: PropTypes.string,
popupComponent: PropTypes.elementType,
popupContent: PropTypes.node,
placeholder: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.string),
PropTypes.string,
]),
};
static defaultProps = {
prefix: 'next-',
rtl: false,
type: 'date',
size: 'medium',
showTime: false,
resetTime: false,
format: 'YYYY-MM-DD',
disabledDate: () => false,
footerRender: () => null,
hasClear: true,
defaultVisible: false,
popupTriggerType: 'click',
popupAlign: 'tl tl',
locale: nextLocale.DatePicker,
disableChangeMode: false,
onChange: func.noop,
onOk: func.noop,
onVisibleChange: func.noop,
};
constructor(props, context) {
super(props, context);
const dateTimeFormat = getDateTimeFormat(
props.format,
props.showTime,
props.type,
);
extend(dateTimeFormat, this);
const val = props.value || props.defaultValue;
const values = getFormatValues(val, this.dateTimeFormat);
this.inputAsString =
val && (typeof val[0] === 'string' || typeof val[1] === 'string');
this.state = {
visible: props.visible || props.defaultVisible,
startValue: values[0],
endValue: values[1],
startDateInputStr: '',
endDateInputStr: '',
activeDateInput: 'startValue',
startTimeInputStr: '',
endTimeInputStr: '',
inputing: false, // 当前是否处于输入状态
panel: PANEL.DATE,
};
}
componentWillReceiveProps(nextProps) {
if ('showTime' in nextProps) {
const dateTimeFormat = getDateTimeFormat(
nextProps.format || this.props.format,
nextProps.showTime,
nextProps.type,
);
extend(dateTimeFormat, this);
}
if ('value' in nextProps) {
const values = getFormatValues(
nextProps.value,
this.dateTimeFormat,
);
this.setState({
startValue: values[0],
endValue: values[1],
});
this.inputAsString =
nextProps.value &&
(typeof nextProps.value[0] === 'string' ||
typeof nextProps.value[1] === 'string');
}
if ('visible' in nextProps) {
this.setState({
visible: nextProps.visible,
});
}
}
onValueChange = (values, handler = 'onChange') => {
let ret;
if (!values.length || !this.inputAsString) {
ret = values;
} else {
ret = [
values[0] ? values[0].format(this.dateTimeFormat) : null,
values[1] ? values[1].format(this.dateTimeFormat) : null,
];
}
this.props[handler](ret);
};
onSelectCalendarPanel = (value, active) => {
const { showTime, resetTime } = this.props;
const {
activeDateInput: prevActiveDateInput,
startValue: prevStartValue,
endValue: prevEndValue,
} = this.state;
const newState = {
activeDateInput: active || prevActiveDateInput,
inputing: false,
};
let newValue = value;
switch (active || prevActiveDateInput) {
case 'startValue': {
if (
!prevEndValue ||
value.valueOf() <= prevEndValue.valueOf()
) {
newState.activeDateInput = 'endValue';
}
if (showTime) {
if (!prevStartValue) {
// 第一次选择,如果设置了时间默认值,则使用该默认时间
if (showTime.defaultValue) {
const defaultTimeValue = formatDateValue(
Array.isArray(showTime.defaultValue)
? showTime.defaultValue[0]
: showTime.defaultValue,
this.timeFormat,
);
newValue = resetValueTime(value, defaultTimeValue);
}
} else if (!resetTime) {
// 非第一次选择,如果开启了 resetTime ,则记住之前选择的时间值
newValue = resetValueTime(value, prevStartValue);
}
}
newState.startValue = newValue;
if (prevEndValue && value.valueOf() > prevEndValue.valueOf()) {
newState.endValue = null;
newState.activeDateInput = 'endValue';
}
break;
}
case 'endValue':
if (!prevStartValue) {
newState.activeDateInput = 'startValue';
}
if (showTime) {
if (!prevEndValue) {
// 第一次选择,如果设置了时间默认值,则使用该默认时间
if (showTime.defaultValue) {
const defaultTimeValue = formatDateValue(
Array.isArray(showTime.defaultValue)
? showTime.defaultValue[1] ||
showTime.defaultValue[0]
: showTime.defaultValue,
this.timeFormat,
);
newValue = resetValueTime(value, defaultTimeValue);
}
} else if (!resetTime) {
// 非第一次选择,如果开启了 resetTime ,则记住之前选择的时间值
newValue = resetValueTime(value, prevEndValue);
}
}
newState.endValue = newValue;
// 选择了一个比开始日期更小的结束日期,此时表示用户重新选择了
if (
prevStartValue &&
value.valueOf() < prevStartValue.valueOf()
) {
newState.startValue = value;
newState.endValue = null;
}
break;
}
const newStartValue =
'startValue' in newState ? newState.startValue : prevStartValue;
const newEndValue =
'endValue' in newState ? newState.endValue : prevEndValue;
// 受控状态选择不更新值
if ('value' in this.props) {
delete newState.startValue;
delete newState.endValue;
}
this.setState(newState);
this.onValueChange([newStartValue, newEndValue]);
};
clearRange = () => {
this.setState({
startDateInputStr: '',
endDateInputStr: '',
startTimeInputStr: '',
endTimeInputStr: '',
});
if (!('value' in this.props)) {
this.setState({
startValue: null,
endValue: null,
});
}
this.onValueChange([]);
};
onDateInputChange = (inputStr, e, eventType) => {
if (eventType === 'clear' || !inputStr) {
e.stopPropagation();
this.clearRange();
} else {
const stateName = mapInputStateName(this.state.activeDateInput);
this.setState({
[stateName]: inputStr,
inputing: this.state.activeDateInput,
});
}
};
onDateInputBlur = () => {
const stateName = mapInputStateName(this.state.activeDateInput);
const dateInputStr = this.state[stateName];
if (dateInputStr) {
const { format, disabledDate } = this.props;
const parsed = moment(dateInputStr, format, true);
this.setState({
[stateName]: '',
inputing: false,
});
if (parsed.isValid() && !disabledDate(parsed, 'date')) {
const valueName = this.state.activeDateInput;
const newValue = parsed;
this.handleChange(valueName, newValue);
}
}
};
onDateInputKeyDown = e => {
const { type } = this.props;
const { activeDateInput } = this.state;
const stateName = mapInputStateName(activeDateInput);
const dateInputStr = this.state[stateName];
const dateStr = onDateKeydown(
e,
{
format: this.format,
value: this.state[activeDateInput],
dateInputStr,
},
type === 'date' ? 'day' : type,
);
if (!dateStr) return;
return this.onDateInputChange(dateStr);
};
onFocusDateInput = type => {
if (type !== this.state.activeDateInput) {
this.setState({
activeDateInput: type,
});
}
if (this.state.panel !== PANEL.DATE) {
this.setState({
panel: PANEL.DATE,
});
}
};
onFocusTimeInput = type => {
if (type !== this.state.activeDateInput) {
this.setState({
activeDateInput: type,
});
}
if (this.state.panel !== PANEL.TIME) {
this.setState({
panel: PANEL.TIME,
});
}
};
onSelectStartTime = value => {
if (!('value' in this.props)) {
this.setState({
startValue: value,
inputing: false,
activeDateInput: 'startTime',
});
}
if (value.valueOf() !== this.state.startValue.valueOf()) {
this.onValueChange([value, this.state.endValue]);
}
};
onSelectEndTime = value => {
if (!('value' in this.props)) {
this.setState({
endValue: value,
inputing: false,
activeDateInput: 'endTime',
});
}
if (value.valueOf() !== this.state.endValue.valueOf()) {
this.onValueChange([this.state.startValue, value]);
}
};
onTimeInputChange = inputStr => {
const stateName = mapInputStateName(this.state.activeDateInput);
this.setState({
[stateName]: inputStr,
inputing: this.state.activeDateInput,
});
};
onTimeInputBlur = () => {
const stateName = mapInputStateName(this.state.activeDateInput);
const timeInputStr = this.state[stateName];
if (timeInputStr) {
const parsed = moment(timeInputStr, this.timeFormat, true);
this.setState({
[stateName]: '',
inputing: false,
});
if (parsed.isValid()) {
const hour = parsed.hour();
const minute = parsed.minute();
const second = parsed.second();
const valueName = mapTimeToValue(this.state.activeDateInput);
const newValue = this.state[valueName]
.clone()
.hour(hour)
.minute(minute)
.second(second);
this.handleChange(valueName, newValue);
}
}
};
onTimeInputKeyDown = e => {
const { showTime } = this.props;
const { activeDateInput } = this.state;
const stateName = mapInputStateName(activeDateInput);
const timeInputStr = this.state[stateName];
const {
disabledMinutes,
disabledSeconds,
hourStep = 1,
minuteStep = 1,
secondStep = 1,
} = typeof showTime === 'object' ? showTime : {};
let unit = 'second';
if (disabledSeconds) {
unit = disabledMinutes ? 'hour' : 'minute';
}
const timeStr = onTimeKeydown(
e,
{
format: this.timeFormat,
timeInputStr,
value: this.state[
activeDateInput.indexOf('start') ? 'startValue' : 'endValue'
],
steps: {
hour: hourStep,
minute: minuteStep,
second: secondStep,
},
},
unit,
);
if (!timeStr) return;
this.onTimeInputChange(timeStr);
};
handleChange = (valueName, newValue) => {
if (!('value' in this.props)) {
this.setState({
[valueName]: newValue,
});
}
const startValue =
valueName === 'startValue' ? newValue : this.state.startValue;
const endValue =
valueName === 'endValue' ? newValue : this.state.endValue;
this.onValueChange([startValue, endValue]);
};
onVisibleChange = (visible, type) => {
if (!('visible' in this.props)) {
this.setState({
visible,
});
}
this.props.onVisibleChange(visible, type);
};
changePanel = panel => {
const { startValue, endValue } = this.state;
this.setState({
panel,
activeDateInput:
panel === PANEL.DATE
? !!startValue && !endValue
? 'endValue'
: 'startValue'
: 'startTime',
});
};
onOk = () => {
this.onVisibleChange(false, 'okBtnClick');
this.onValueChange(
[this.state.startValue, this.state.endValue],
'onOk',
);
};
// 如果用户没有给定时间禁用逻辑,则给默认到禁用逻辑,即如果是同一天,则时间不能是同样的
getDisabledTime = ({ startValue, endValue }) => {
const { disabledHours, disabledMinutes, disabledSeconds } =
this.props.showTime || {};
let disabledTime = {};
if (startValue && endValue) {
const isSameDay = startValue.format('L') === endValue.format('L');
const newDisabledHours = isFunction(disabledHours)
? disabledHours
: index => {
if (isSameDay && index < startValue.hour()) {
return true;
}
};
const newDisabledMinutes = isFunction(disabledMinutes)
? disabledMinutes
: index => {
if (
isSameDay &&
startValue.hour() === endValue.hour() &&
index < startValue.minute()
) {
return true;
}
};
const newDisabledSeconds = isFunction(disabledSeconds)
? disabledSeconds
: index => {
if (
isSameDay &&
startValue.hour() === endValue.hour() &&
startValue.minute() === endValue.minute() &&
index <= startValue.second()
) {
return true;
}
};
disabledTime = {
disabledHours: newDisabledHours,
disabledMinutes: newDisabledMinutes,
disabledSeconds: newDisabledSeconds,
};
}
return disabledTime;
};
renderPreview([startValue, endValue], others) {
const { prefix, format, className, renderPreview } = this.props;
const previewCls = classnames(className, `${prefix}form-preview`);
const startLabel = startValue ? startValue.format(format) : '';
const endLabel = endValue ? endValue.format(format) : '';
if (typeof renderPreview === 'function') {
return (
<div {...others} className={previewCls}>
{renderPreview([startValue, endValue], this.props)}
</div>
);
}
return (
<p {...others} className={previewCls}>
{startLabel} - {endLabel}
</p>
);
}
render() {
const {
prefix,
rtl,
type,
defaultVisibleMonth,
onVisibleMonthChange,
showTime,
disabledDate,
footerRender,
label,
ranges = {}, // 兼容0.x ranges 属性
state: inputState,
size,
disabled,
hasClear,
popupTriggerType,
popupAlign,
popupContainer,
popupStyle,
popupClassName,
popupProps,
popupComponent,
popupContent,
followTrigger,
className,
locale,
inputProps,
dateCellRender,
monthCellRender,
yearCellRender,
startDateInputAriaLabel,
startTimeInputAriaLabel,
endDateInputAriaLabel,
endTimeInputAriaLabel,
isPreview,
disableChangeMode,
yearRange,
placeholder,
...others
} = this.props;
const { state } = this;
const classNames = classnames(
{
[`${prefix}range-picker`]: true,
[`${prefix}${size}`]: size,
[`${prefix}disabled`]: disabled,
},
className,
);
const panelBodyClassName = classnames({
[`${prefix}range-picker-body`]: true,
[`${prefix}range-picker-body-show-time`]: showTime,
});
const triggerCls = classnames({
[`${prefix}range-picker-trigger`]: true,
[`${prefix}error`]: inputState === 'error',
});
const startDateInputCls = classnames({
[`${prefix}range-picker-panel-input-start-date`]: true,
[`${prefix}focus`]: state.activeDateInput === 'startValue',
});
const endDateInputCls = classnames({
[`${prefix}range-picker-panel-input-end-date`]: true,
[`${prefix}focus`]: state.activeDateInput === 'endValue',
});
if (rtl) {
others.dir = 'rtl';
}
if (isPreview) {
return this.renderPreview(
[state.startValue, state.endValue],
obj.pickOthers(others, RangePicker.PropTypes),
);
}
const startDateInputValue =
state.inputing === 'startValue'
? state.startDateInputStr
: (state.startValue && state.startValue.format(this.format)) ||
'';
const endDateInputValue =
state.inputing === 'endValue'
? state.endDateInputStr
: (state.endValue && state.endValue.format(this.format)) || '';
let startTriggerValue = startDateInputValue;
let endTriggerValue = endDateInputValue;
const sharedInputProps = {
...inputProps,
size,
disabled,
onChange: this.onDateInputChange,
onBlur: this.onDateInputBlur,
onPressEnter: this.onDateInputBlur,
onKeyDown: this.onDateInputKeyDown,
};
const startDateInput = (
<Input
{...sharedInputProps}
aria-label={startDateInputAriaLabel}
placeholder={this.format}
value={startDateInputValue}
onFocus={() => this.onFocusDateInput('startValue')}
className={startDateInputCls}
/>
);
const endDateInput = (
<Input
{...sharedInputProps}
aria-label={endDateInputAriaLabel}
placeholder={this.format}
value={endDateInputValue}
onFocus={() => this.onFocusDateInput('endValue')}
className={endDateInputCls}
/>
);
const shareCalendarProps = {
showOtherMonth: true,
dateCellRender,
monthCellRender,
yearCellRender,
format: this.format,
defaultVisibleMonth,
onVisibleMonthChange,
};
const datePanel =
type === 'date' ? (
<RangeCalendar
{...shareCalendarProps}
yearRange={yearRange}
disableChangeMode={disableChangeMode}
disabledDate={disabledDate}
onSelect={this.onSelectCalendarPanel}
startValue={state.startValue}
endValue={state.endValue}
/>
) : (
<div className={`${prefix}range-picker-panel-body`}>
<Calendar
shape="panel"
modes={type === 'month' ? ['month', 'year'] : ['year']}
{...{ ...shareCalendarProps }}
disabledDate={date => {
return (
state.endValue &&
date.isAfter(state.endValue, type)
);
}}
onSelect={value => {
const selectedValue = value
.clone()
.date(1)
.hour(0)
.minute(0)
.second(0);
if (type === 'year') {
selectedValue.month(0);
}
this.onSelectCalendarPanel(
selectedValue,
'startValue',
);
}}
value={state.startValue}
/>
<Calendar
shape="panel"
modes={type === 'month' ? ['month', 'year'] : ['year']}
{...shareCalendarProps}
disabledDate={date => {
return (
state.startValue &&
date.isBefore(state.startValue, type)
);
}}
onSelect={value => {
const selectedValue = value
.clone()
.hour(23)
.minute(59)
.second(59);
if (type === 'year') {
selectedValue.month(11).date(31);
} else {
selectedValue.date(selectedValue.daysInMonth());
}
this.onSelectCalendarPanel(
selectedValue,
'endValue',
);
}}
value={state.endValue}
/>
</div>
);
let startTimeInput = null;
let endTimeInput = null;
let timePanel = null;
let panelFooter = footerRender();
if (showTime) {
const startTimeInputValue =
state.inputing === 'startTime'
? state.startTimeInputStr
: (state.startValue &&
state.startValue.format(this.timeFormat)) ||
'';
const endTimeInputValue =
state.inputing === 'endTime'
? state.endTimeInputStr
: (state.endValue &&
state.endValue.format(this.timeFormat)) ||
'';
startTriggerValue =
(state.startValue &&
state.startValue.format(this.dateTimeFormat)) ||
'';
endTriggerValue =
(state.endValue &&
state.endValue.format(this.dateTimeFormat)) ||
'';
const sharedTimeInputProps = {
size,
placeholder: this.timeFormat,
onFocus: this.onFocusTimeInput,
onBlur: this.onTimeInputBlur,
onPressEnter: this.onTimeInputBlur,
onChange: this.onTimeInputChange,
onKeyDown: this.onTimeInputKeyDown,
};
const startTimeInputCls = classnames({
[`${prefix}range-picker-panel-input-start-time`]: true,
[`${prefix}focus`]: state.activeDateInput === 'startTime',
});
startTimeInput = (
<Input
{...sharedTimeInputProps}
value={startTimeInputValue}
aria-label={startTimeInputAriaLabel}
disabled={disabled || !state.startValue}
onFocus={() => this.onFocusTimeInput('startTime')}
className={startTimeInputCls}
/>
);
const endTimeInputCls = classnames({
[`${prefix}range-picker-panel-input-end-time`]: true,
[`${prefix}focus`]: state.activeDateInput === 'endTime',
});
endTimeInput = (
<Input
{...sharedTimeInputProps}
value={endTimeInputValue}
aria-label={endTimeInputAriaLabel}
disabled={disabled || !state.endValue}
onFocus={() => this.onFocusTimeInput('endTime')}
className={endTimeInputCls}
/>
);
const showSecond = this.timeFormat.indexOf('s') > -1;
const showMinute = this.timeFormat.indexOf('m') > -1;
const sharedTimePickerProps = {
...showTime,
prefix,
locale,
disabled,
showSecond,
showMinute,
};
const disabledTime = this.getDisabledTime(state);
timePanel = (
<div className={`${prefix}range-picker-panel-time`}>
<TimePickerPanel
{...sharedTimePickerProps}
disabled={disabled || !state.startValue}
className={`${prefix}range-picker-panel-time-start`}
value={state.startValue}
onSelect={this.onSelectStartTime}
/>
<TimePickerPanel
{...sharedTimePickerProps}
{...disabledTime}
disabled={disabled || !state.endValue}
className={`${prefix}range-picker-panel-time-end`}
value={state.endValue}
onSelect={this.onSelectEndTime}
/>
</div>
);
}
panelFooter = panelFooter || (
<PanelFooter
prefix={prefix}
value={state.startValue || state.endValue}
ranges={Object.keys(ranges).map(key => ({
label: key,
value: ranges[key],
onChange: values => {
this.setState({
startValue: values[0],
endValue: values[1],
});
this.onValueChange(values);
},
}))}
disabledOk={
!state.startValue ||
!state.endValue ||
state.startValue.valueOf() > state.endValue.valueOf()
}
locale={locale}
panel={state.panel}
onPanelChange={showTime ? this.changePanel : null}
onOk={this.onOk}
/>
);
const panelBody = {
[PANEL.DATE]: datePanel,
[PANEL.TIME]: timePanel,
}[state.panel];
const allowClear = state.startValue && state.endValue && hasClear;
let [startPlaceholder, endPlaceholder] = placeholder || [];
if (typeof placeholder === 'string') {
startPlaceholder = placeholder;
endPlaceholder = placeholder;
}
const trigger = (
<div className={triggerCls}>
<Input
{...sharedInputProps}
readOnly
role="combobox"
aria-expanded={state.visible}
label={label}
placeholder={startPlaceholder || locale.startPlaceholder}
value={startTriggerValue}
hasBorder={false}
className={`${prefix}range-picker-trigger-input`}
onFocus={() => this.onFocusDateInput('startValue')}
/>
<span className={`${prefix}range-picker-trigger-separator`}>
-
</span>
<Input
{...sharedInputProps}
readOnly
role="combobox"
aria-expanded={state.visible}
placeholder={endPlaceholder || locale.endPlaceholder}
value={endTriggerValue}
hasBorder={false}
className={`${prefix}range-picker-trigger-input`}
onFocus={() => this.onFocusDateInput('endValue')}
hasClear={allowClear}
hint="calendar"
/>
</div>
);
const PopupComponent = popupComponent || Popup;
return (
<div
{...obj.pickOthers(RangePicker.propTypes, others)}
className={classNames}
>
<PopupComponent
autoFocus
align={popupAlign}
{...popupProps}
followTrigger={followTrigger}
disabled={disabled}
visible={state.visible}
onVisibleChange={this.onVisibleChange}
triggerType={popupTriggerType}
container={popupContainer}
style={popupStyle}
className={popupClassName}
trigger={trigger}
>
{popupContent || (
<div dir={others.dir} className={panelBodyClassName}>
<div
className={`${prefix}range-picker-panel-header`}
>
<div
className={`${prefix}range-picker-panel-input`}
>
{startDateInput}
{startTimeInput}
<span
className={`${prefix}range-picker-panel-input-separator`}
>
-
</span>
{endDateInput}
{endTimeInput}
</div>
</div>
{panelBody}
{panelFooter}
</div>
)}
</PopupComponent>
</div>
);
}
}