mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
添加季度选择器 Quarter (#1382)
* quarter 相关 * 优化 time 选择器 * 优化 time 选择器样式 * quarter 部分代码 * 添加 Quarter 组件 * rm debugger * chart 默认不可见时不销毁
This commit is contained in:
parent
dbf3581bb9
commit
625f2f1691
27
docs/components/form/quarter.md
Normal file
27
docs/components/form/quarter.md
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
title: Quarter 季度
|
||||
description:
|
||||
type: 0
|
||||
group: null
|
||||
menuName: Quarter 季度
|
||||
icon:
|
||||
order: 62
|
||||
---
|
||||
|
||||
## 基本用法
|
||||
|
||||
```schema:height="400" scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"api": "https://houtai.baidu.com/api/mock2/form/saveForm",
|
||||
"controls": [
|
||||
{
|
||||
"type": "quarter",
|
||||
"name": "quarter",
|
||||
"label": "季度"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
更多用法和配置可以参考 [Date 日期](date),quarter 就是 data 的特定配置,所以 data 的所有配置都能使用。
|
@ -685,6 +685,15 @@ export default [
|
||||
import('../../docs/zh-CN/components/form/year.md').then(
|
||||
makeMarkdownRenderer
|
||||
)
|
||||
},
|
||||
{
|
||||
label: 'Quarter 年',
|
||||
path: '/zh-CN/docs/components/form/quarter',
|
||||
getComponent: () =>
|
||||
// @ts-ignore
|
||||
import('../../docs/zh-CN/components/form/quarter.md').then(
|
||||
makeMarkdownRenderer
|
||||
)
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -193,24 +193,6 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input {
|
||||
outline: none;
|
||||
width: 42px;
|
||||
font-size: var(--Calendar-input-fontSize);
|
||||
color: var(--Calendar-input-color);
|
||||
border: 1px solid var(--Calendar-input-borderColor);
|
||||
border-radius: var(--Calendar-input-borderRadius);
|
||||
height: var(--Calendar-input-height);
|
||||
line-height: var(--Calendar-input-lineHeight);
|
||||
padding: var(--Calendar-input-paddingY) var(--Calendar-input-paddingX);
|
||||
box-shadow: none;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--Calendar-input-onFocused-borderColor);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.rdtActions {
|
||||
margin-top: var(--gap-sm);
|
||||
text-align: right;
|
||||
@ -218,18 +200,87 @@
|
||||
}
|
||||
}
|
||||
|
||||
.rdtCounter {
|
||||
.rdtBtn {
|
||||
height: 30%;
|
||||
line-height: px2rem(20px);
|
||||
// .rdtCounter {
|
||||
// .rdtBtn {
|
||||
// height: 30%;
|
||||
// line-height: px2rem(20px);
|
||||
// }
|
||||
|
||||
// .rdtCount {
|
||||
// height: 40%;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// justify-content: center;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
.#{$ns}CalendarInput {
|
||||
outline: none;
|
||||
width: 40px;
|
||||
font-size: var(--Calendar-input-fontSize);
|
||||
color: var(--Calendar-input-color);
|
||||
border: 1px solid var(--Calendar-input-borderColor);
|
||||
border-radius: var(--Calendar-input-borderRadius);
|
||||
height: var(--Calendar-input-height);
|
||||
line-height: var(--Calendar-input-lineHeight);
|
||||
padding: var(--Calendar-input-paddingY) var(--Calendar-input-paddingX);
|
||||
box-shadow: none;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--Calendar-input-onFocused-borderColor);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}CalendarTime {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.#{$ns}CalendarCounter {
|
||||
&-btn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 25px;
|
||||
text-align: center;
|
||||
color: var(--Button--primary-bg);
|
||||
|
||||
&:hover {
|
||||
color: var(--Button--primary-onActive-bg);
|
||||
}
|
||||
|
||||
.rdtCount {
|
||||
height: 40%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
> svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
&--up > svg {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
&--down > svg {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
&-sep {
|
||||
width: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&--daypart {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
&--daypart &-value {
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,7 +398,8 @@
|
||||
}
|
||||
|
||||
td.rdtMonth,
|
||||
td.rdtYear {
|
||||
td.rdtYear,
|
||||
td.rdtQuarter {
|
||||
width: px2rem(50px);
|
||||
height: px2rem(40px);
|
||||
|
||||
|
@ -228,7 +228,7 @@ export type ShortCuts =
|
||||
| ShortCutDateRange;
|
||||
|
||||
export interface DateProps extends LocaleProps, ThemeProps {
|
||||
viewMode: 'years' | 'months' | 'days' | 'time';
|
||||
viewMode: 'years' | 'months' | 'days' | 'time' | 'quarters';
|
||||
className?: string;
|
||||
placeholder?: string;
|
||||
inputFormat?: string;
|
||||
@ -248,7 +248,23 @@ export interface DateProps extends LocaleProps, ThemeProps {
|
||||
minTime?: moment.Moment;
|
||||
maxTime?: moment.Moment;
|
||||
dateFormat?: string;
|
||||
timeConstraints?: any;
|
||||
timeConstraints?: {
|
||||
hours?: {
|
||||
min: number;
|
||||
max: number;
|
||||
step: number;
|
||||
};
|
||||
minutes?: {
|
||||
min: number;
|
||||
max: number;
|
||||
step: number;
|
||||
};
|
||||
seconds: {
|
||||
min: number;
|
||||
max: number;
|
||||
step: number;
|
||||
};
|
||||
};
|
||||
popOverContainer?: any;
|
||||
|
||||
// 是否为内嵌模式,如果开启就不是 picker 了,直接页面点选。
|
||||
@ -381,7 +397,8 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
dateFormat,
|
||||
timeFormat,
|
||||
closeOnSelect,
|
||||
utc
|
||||
utc,
|
||||
viewMode
|
||||
} = this.props;
|
||||
|
||||
if (!moment.isMoment(value)) {
|
||||
|
@ -577,6 +577,8 @@ export class DateRangePicker extends React.Component<
|
||||
embed
|
||||
} = this.props;
|
||||
const __ = this.props.translate;
|
||||
let viewMode: 'days' | 'months' | 'years' | 'months' | 'days' | 'time' =
|
||||
'days';
|
||||
|
||||
const {startDate, endDate} = this.state;
|
||||
return (
|
||||
@ -591,7 +593,7 @@ export class DateRangePicker extends React.Component<
|
||||
dateFormat={dateFormat}
|
||||
timeFormat={timeFormat}
|
||||
isValidDate={this.checkStartIsValidDate}
|
||||
viewMode="days"
|
||||
viewMode={viewMode}
|
||||
input={false}
|
||||
onClose={this.close}
|
||||
renderDay={this.renderDay}
|
||||
@ -608,7 +610,7 @@ export class DateRangePicker extends React.Component<
|
||||
viewDate={this.nextMonth}
|
||||
isEndDate
|
||||
isValidDate={this.checkEndIsValidDate}
|
||||
viewMode="days"
|
||||
viewMode={viewMode}
|
||||
input={false}
|
||||
onClose={this.close}
|
||||
renderDay={this.renderDay}
|
||||
|
@ -6,8 +6,11 @@ import React from 'react';
|
||||
import CustomCalendarContainer from './CalendarContainer';
|
||||
import cx from 'classnames';
|
||||
import moment from 'moment';
|
||||
import {themeable, ThemeOutterProps, ThemeProps} from '../../theme';
|
||||
|
||||
interface BaseDatePickerProps extends ReactDatePicker.DatetimepickerProps {
|
||||
interface BaseDatePickerProps
|
||||
extends Omit<ReactDatePicker.DatetimepickerProps, 'viewMode'> {
|
||||
viewMode?: 'years' | 'months' | 'days' | 'time' | 'quarters';
|
||||
inputFormat?: string;
|
||||
onViewModeChange?: (type: string) => void;
|
||||
requiredConfirm?: boolean;
|
||||
@ -22,8 +25,41 @@ interface BaseDatePickerProps extends ReactDatePicker.DatetimepickerProps {
|
||||
|
||||
class BaseDatePicker extends ReactDatePicker {
|
||||
state: any;
|
||||
props: BaseDatePickerProps;
|
||||
props: any;
|
||||
setState: (state: any) => void;
|
||||
getStateFromProps: any;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
const state = this.getStateFromProps(this.props);
|
||||
|
||||
if (state.open === undefined) {
|
||||
state.open = !this.props.input;
|
||||
}
|
||||
|
||||
state.currentView = this.props.dateFormat
|
||||
? this.props.viewMode || state.updateOn || 'days'
|
||||
: 'time';
|
||||
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
static propTypes = {};
|
||||
|
||||
getUpdateOn = (formats: any) => {
|
||||
if (formats.date.match(/[lLD]/)) {
|
||||
return 'days';
|
||||
} else if (formats.date.indexOf('M') !== -1) {
|
||||
return 'months';
|
||||
} else if (formats.date.indexOf('Q') !== -1) {
|
||||
return 'quarters';
|
||||
} else if (formats.date.indexOf('Y') !== -1) {
|
||||
return 'years';
|
||||
}
|
||||
|
||||
return 'days';
|
||||
};
|
||||
|
||||
getComponentProps = ((origin: Function) => {
|
||||
return () => {
|
||||
const props = origin.call(this);
|
||||
@ -37,22 +73,29 @@ class BaseDatePicker extends ReactDatePicker {
|
||||
'classPrefix',
|
||||
'prevIcon',
|
||||
'nextIcon',
|
||||
'isEndDate'
|
||||
'isEndDate',
|
||||
'classnames'
|
||||
].forEach(key => (props[key] = (this.props as any)[key]));
|
||||
|
||||
return props;
|
||||
};
|
||||
})((this as any).getComponentProps);
|
||||
|
||||
setDate = (type: 'month' | 'year') => {
|
||||
setDate = (type: 'month' | 'year' | 'quarters') => {
|
||||
// todo 没看懂这个是啥意思,好像没啥用
|
||||
const currentShould =
|
||||
this.props.viewMode === 'months' &&
|
||||
!/^mm$/i.test(this.props.inputFormat || '');
|
||||
const nextViews = {
|
||||
month: currentShould ? 'months' : 'days',
|
||||
year: currentShould ? 'months' : 'days'
|
||||
year: currentShould ? 'months' : 'days',
|
||||
quarters: ''
|
||||
};
|
||||
|
||||
if ((this.props.viewMode as any) === 'quarters') {
|
||||
nextViews.year = 'quarters';
|
||||
}
|
||||
|
||||
return (e: any) => {
|
||||
this.setState({
|
||||
viewDate: this.state.viewDate
|
||||
@ -67,6 +110,67 @@ class BaseDatePicker extends ReactDatePicker {
|
||||
};
|
||||
};
|
||||
|
||||
updateSelectedDate = (e: React.MouseEvent, close?: boolean) => {
|
||||
const that: any = this;
|
||||
let target = e.currentTarget,
|
||||
modifier = 0,
|
||||
viewDate = this.state.viewDate,
|
||||
currentDate = this.state.selectedDate || viewDate,
|
||||
date;
|
||||
|
||||
if (target.className.indexOf('rdtDay') !== -1) {
|
||||
if (target.className.indexOf('rdtNew') !== -1) modifier = 1;
|
||||
else if (target.className.indexOf('rdtOld') !== -1) modifier = -1;
|
||||
|
||||
date = viewDate
|
||||
.clone()
|
||||
.month(viewDate.month() + modifier)
|
||||
.date(parseInt(target.getAttribute('data-value')!, 10));
|
||||
} else if (target.className.indexOf('rdtMonth') !== -1) {
|
||||
date = viewDate
|
||||
.clone()
|
||||
.month(parseInt(target.getAttribute('data-value')!, 10))
|
||||
.date(currentDate.date());
|
||||
} else if (target.className.indexOf('rdtQuarter') !== -1) {
|
||||
date = viewDate
|
||||
.clone()
|
||||
.quarter(parseInt(target.getAttribute('data-value')!, 10))
|
||||
.date(currentDate.date());
|
||||
} else if (target.className.indexOf('rdtYear') !== -1) {
|
||||
date = viewDate
|
||||
.clone()
|
||||
.month(currentDate.month())
|
||||
.date(currentDate.date())
|
||||
.year(parseInt(target.getAttribute('data-value')!, 10));
|
||||
}
|
||||
|
||||
date
|
||||
.hours(currentDate.hours())
|
||||
.minutes(currentDate.minutes())
|
||||
.seconds(currentDate.seconds())
|
||||
.milliseconds(currentDate.milliseconds());
|
||||
|
||||
if (!this.props.value) {
|
||||
var open = !(this.props.closeOnSelect && close);
|
||||
if (!open) {
|
||||
that.props.onBlur(date);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selectedDate: date,
|
||||
viewDate: date.clone().startOf('month'),
|
||||
inputValue: date.format(this.state.inputFormat),
|
||||
open: open
|
||||
});
|
||||
} else {
|
||||
if (this.props.closeOnSelect && close) {
|
||||
that.closeCalendar();
|
||||
}
|
||||
}
|
||||
|
||||
that.props.onChange(date);
|
||||
};
|
||||
|
||||
render() {
|
||||
const Component = CustomCalendarContainer as any;
|
||||
return (
|
||||
@ -82,5 +186,5 @@ class BaseDatePicker extends ReactDatePicker {
|
||||
}
|
||||
}
|
||||
|
||||
const Calendar: any = BaseDatePicker;
|
||||
const Calendar: any = themeable(BaseDatePicker as any);
|
||||
export default Calendar as React.ComponentType<BaseDatePickerProps>;
|
||||
|
@ -4,12 +4,16 @@ import CalendarContainer from 'react-datetime/src/CalendarContainer';
|
||||
import CustomDaysView from './DaysView';
|
||||
import CustomYearsView from './YearsView';
|
||||
import CustomMonthsView from './MonthsView';
|
||||
import CustomTimeView from './TimeView';
|
||||
import QuartersView from './QuartersView';
|
||||
|
||||
export default class CustomCalendarContainer extends CalendarContainer {
|
||||
viewComponents: any = {
|
||||
...(this as any).viewComponents,
|
||||
days: CustomDaysView,
|
||||
years: CustomYearsView,
|
||||
months: CustomMonthsView
|
||||
months: CustomMonthsView,
|
||||
time: CustomTimeView,
|
||||
quarters: QuartersView
|
||||
};
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import moment from 'moment';
|
||||
import DaysView from 'react-datetime/src/DaysView';
|
||||
import React from 'react';
|
||||
import {LocaleProps, localeable} from '../../locale';
|
||||
import {ClassNamesFn} from '../../theme';
|
||||
|
||||
interface CustomDaysViewProps extends LocaleProps {
|
||||
classPrefix?: string;
|
||||
@ -35,6 +36,7 @@ interface CustomDaysViewProps extends LocaleProps {
|
||||
showView: (view: string) => () => void;
|
||||
updateSelectedDate: (event: React.MouseEvent<any>, close?: boolean) => void;
|
||||
handleClickOutside: () => void;
|
||||
classnames: ClassNamesFn;
|
||||
}
|
||||
|
||||
export class CustomDaysView extends DaysView {
|
||||
@ -112,7 +114,13 @@ export class CustomDaysView extends DaysView {
|
||||
};
|
||||
|
||||
renderTimes = () => {
|
||||
const {timeFormat, selectedDate, viewDate, isEndDate} = this.props;
|
||||
const {
|
||||
timeFormat,
|
||||
selectedDate,
|
||||
viewDate,
|
||||
isEndDate,
|
||||
classnames: cx
|
||||
} = this.props;
|
||||
|
||||
const date = selectedDate || (isEndDate ? viewDate.endOf('day') : viewDate);
|
||||
const inputs: Array<React.ReactNode> = [];
|
||||
@ -131,6 +139,7 @@ export class CustomDaysView extends DaysView {
|
||||
key={i + 'input'}
|
||||
type="text"
|
||||
value={date.format(format)}
|
||||
className={cx('CalendarInput')}
|
||||
min={min}
|
||||
max={max}
|
||||
onChange={e =>
|
||||
|
@ -47,8 +47,8 @@ export class CustomMonthsView extends MonthsView {
|
||||
|
||||
return (
|
||||
<div className="rdtMonths">
|
||||
<table>
|
||||
{showYearHead && (
|
||||
{showYearHead && (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
@ -78,8 +78,9 @@ export class CustomMonthsView extends MonthsView {
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
)}
|
||||
</table>
|
||||
</table>
|
||||
)}
|
||||
|
||||
<table>
|
||||
<tbody>{this.renderMonths()}</tbody>
|
||||
</table>
|
||||
|
171
src/components/calendar/QuartersView.tsx
Normal file
171
src/components/calendar/QuartersView.tsx
Normal file
@ -0,0 +1,171 @@
|
||||
import React from 'react';
|
||||
import {localeable, LocaleProps} from '../../locale';
|
||||
import {ThemeProps} from '../../theme';
|
||||
|
||||
export interface QuarterViewProps extends LocaleProps, ThemeProps {
|
||||
viewDate: moment.Moment;
|
||||
selectedDate: moment.Moment;
|
||||
inputFormat?: string;
|
||||
updateOn: string;
|
||||
subtractTime: (
|
||||
amount: number,
|
||||
type: string,
|
||||
toSelected?: moment.Moment
|
||||
) => () => void;
|
||||
addTime: (
|
||||
amount: number,
|
||||
type: string,
|
||||
toSelected?: moment.Moment
|
||||
) => () => void;
|
||||
setDate: (type: string) => () => void;
|
||||
showView: (view: string) => () => void;
|
||||
updateSelectedDate: (e: any, close?: boolean) => void;
|
||||
renderQuarter: any;
|
||||
isValidDate: any;
|
||||
}
|
||||
|
||||
export class QuarterView extends React.Component<QuarterViewProps> {
|
||||
alwaysValidDate: any;
|
||||
|
||||
renderYear() {
|
||||
const __ = this.props.translate;
|
||||
const showYearHead = !/^mm$/i.test(this.props.inputFormat || '');
|
||||
|
||||
if (!showYearHead) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const canClick = /yy/i.test(this.props.inputFormat || '');
|
||||
|
||||
return (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
className="rdtPrev"
|
||||
onClick={this.props.subtractTime(1, 'years')}
|
||||
>
|
||||
«
|
||||
</th>
|
||||
{canClick ? (
|
||||
<th className="rdtSwitch" onClick={this.props.showView('years')}>
|
||||
{this.props.viewDate.format(__('YYYY年'))}
|
||||
</th>
|
||||
) : (
|
||||
<th className="rdtSwitch">
|
||||
{this.props.viewDate.format(__('YYYY年'))}
|
||||
</th>
|
||||
)}
|
||||
|
||||
<th className="rdtNext" onClick={this.props.addTime(1, 'years')}>
|
||||
»
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
renderQuarters() {
|
||||
let date = this.props.selectedDate,
|
||||
month = this.props.viewDate.month(),
|
||||
year = this.props.viewDate.year(),
|
||||
rows = [],
|
||||
i = 1,
|
||||
months = [],
|
||||
renderer = this.props.renderQuarter || this.renderQuarter,
|
||||
isValid = this.props.isValidDate || this.alwaysValidDate,
|
||||
classes,
|
||||
props: any,
|
||||
currentMonth: moment.Moment,
|
||||
isDisabled,
|
||||
noOfDaysInMonth,
|
||||
daysInMonth,
|
||||
validDay,
|
||||
// Date is irrelevant because we're only interested in month
|
||||
irrelevantDate = 1;
|
||||
while (i < 5) {
|
||||
classes = 'rdtQuarter';
|
||||
currentMonth = this.props.viewDate
|
||||
.clone()
|
||||
.set({year: year, quarter: i, date: irrelevantDate});
|
||||
|
||||
noOfDaysInMonth = currentMonth.endOf('quarter').format('Q');
|
||||
daysInMonth = Array.from(
|
||||
{length: parseInt(noOfDaysInMonth, 10)},
|
||||
function (e, i) {
|
||||
return i + 1;
|
||||
}
|
||||
);
|
||||
|
||||
validDay = daysInMonth.find(function (d) {
|
||||
var day = currentMonth.clone().set('date', d);
|
||||
return isValid(day);
|
||||
});
|
||||
|
||||
isDisabled = validDay === undefined;
|
||||
|
||||
if (isDisabled) classes += ' rdtDisabled';
|
||||
|
||||
if (date && i === date.quarter() && year === date.year())
|
||||
classes += ' rdtActive';
|
||||
|
||||
props = {
|
||||
'key': i,
|
||||
'data-value': i,
|
||||
'className': classes
|
||||
};
|
||||
|
||||
if (!isDisabled) {
|
||||
props.onClick =
|
||||
this.props.updateOn === 'quarters'
|
||||
? this.updateSelectedQuarter
|
||||
: this.props.setDate('quarter');
|
||||
}
|
||||
|
||||
months.push(renderer(props, i, year, date && date.clone()));
|
||||
|
||||
if (months.length === 2) {
|
||||
rows.push(
|
||||
React.createElement('tr', {key: month + '_' + rows.length}, months)
|
||||
);
|
||||
months = [];
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
renderQuarter = (
|
||||
props: any,
|
||||
quartar: number,
|
||||
year: number,
|
||||
date: moment.Moment
|
||||
) => {
|
||||
return (
|
||||
<td {...props}>
|
||||
<span>Q{quartar}</span>
|
||||
</td>
|
||||
);
|
||||
};
|
||||
|
||||
updateSelectedQuarter = (event: any) => {
|
||||
this.props.updateSelectedDate(event);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {classnames: cx} = this.props;
|
||||
|
||||
return (
|
||||
<div className={cx('ClalendarQuarter')}>
|
||||
{this.renderYear()}
|
||||
<table>
|
||||
<tbody>{this.renderQuarters()}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default localeable(QuarterView);
|
183
src/components/calendar/TimeView.tsx
Normal file
183
src/components/calendar/TimeView.tsx
Normal file
@ -0,0 +1,183 @@
|
||||
// @ts-ignore
|
||||
import TimeView from 'react-datetime/src/TimeView';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import {LocaleProps, localeable} from '../../locale';
|
||||
import {Icon} from '../..';
|
||||
import {ClassNamesFn} from '../../theme';
|
||||
|
||||
export class CustomTimeView extends TimeView {
|
||||
props: {
|
||||
viewDate: moment.Moment;
|
||||
subtractTime: (
|
||||
amount: number,
|
||||
type: string,
|
||||
toSelected?: moment.Moment
|
||||
) => () => void;
|
||||
addTime: (
|
||||
amount: number,
|
||||
type: string,
|
||||
toSelected?: moment.Moment
|
||||
) => () => void;
|
||||
showView: (view: string) => () => void;
|
||||
timeFormat: string;
|
||||
classnames: ClassNamesFn;
|
||||
setTime: (type: string, value: any) => void;
|
||||
} & LocaleProps;
|
||||
onStartClicking: any;
|
||||
disableContextMenu: any;
|
||||
updateMilli: any;
|
||||
renderHeader: any;
|
||||
pad: any;
|
||||
state: {daypart: any; counters: Array<string>; [propName: string]: any};
|
||||
timeConstraints: any;
|
||||
padValues = {
|
||||
hours: 2,
|
||||
minutes: 2,
|
||||
seconds: 2,
|
||||
milliseconds: 3
|
||||
};
|
||||
|
||||
renderDayPart = () => {
|
||||
const {translate: __, classnames: cx} = this.props;
|
||||
return (
|
||||
<div
|
||||
key="dayPart"
|
||||
className={cx('CalendarCounter CalendarCounter--daypart')}
|
||||
>
|
||||
<span
|
||||
key="up"
|
||||
className={cx('CalendarCounter-btn CalendarCounter-btn--up')}
|
||||
onClick={this.onStartClicking('toggleDayPart', 'hours')}
|
||||
onContextMenu={this.disableContextMenu}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" />
|
||||
</span>
|
||||
<div className={cx('CalendarCounter-value')} key={this.state.daypart}>
|
||||
{__(this.state.daypart)}
|
||||
</div>
|
||||
<span
|
||||
key="down"
|
||||
className={cx('CalendarCounter-btn CalendarCounter-btn--down')}
|
||||
onClick={this.onStartClicking('toggleDayPart', 'hours')}
|
||||
onContextMenu={this.disableContextMenu}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
renderCounter = (type: string) => {
|
||||
const cx = this.props.classnames;
|
||||
if (type !== 'daypart') {
|
||||
var value = this.state[type];
|
||||
if (
|
||||
type === 'hours' &&
|
||||
this.props.timeFormat.toLowerCase().indexOf(' a') !== -1
|
||||
) {
|
||||
value = ((value - 1) % 12) + 1;
|
||||
|
||||
if (value === 0) {
|
||||
value = 12;
|
||||
}
|
||||
}
|
||||
|
||||
const {min, max, step} = this.timeConstraints[type];
|
||||
|
||||
return (
|
||||
<div key={type} className={cx('CalendarCounter')}>
|
||||
<span
|
||||
key="up"
|
||||
className={cx('CalendarCounter-btn CalendarCounter-btn--up')}
|
||||
onMouseDown={this.onStartClicking('increase', type)}
|
||||
onContextMenu={this.disableContextMenu}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" />
|
||||
</span>
|
||||
|
||||
<div key="c" className={cx('CalendarCounter-value')}>
|
||||
<input
|
||||
type="text"
|
||||
value={this.pad(type, value)}
|
||||
className={cx('CalendarInput')}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
onChange={e =>
|
||||
this.props.setTime(
|
||||
type,
|
||||
Math.max(
|
||||
min,
|
||||
Math.min(
|
||||
parseInt(e.currentTarget.value.replace(/\D/g, ''), 10) ||
|
||||
0,
|
||||
max
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<span
|
||||
key="do"
|
||||
className={cx('CalendarCounter-btn CalendarCounter-btn--down')}
|
||||
onMouseDown={this.onStartClicking('decrease', type)}
|
||||
onContextMenu={this.disableContextMenu}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
render() {
|
||||
const counters: Array<JSX.Element | null> = [];
|
||||
const cx = this.props.classnames;
|
||||
|
||||
this.state.counters.forEach(c => {
|
||||
if (counters.length) {
|
||||
counters.push(
|
||||
<div
|
||||
key={`sep${counters.length}`}
|
||||
className={cx('CalendarCounter-sep')}
|
||||
>
|
||||
:
|
||||
</div>
|
||||
);
|
||||
}
|
||||
counters.push(this.renderCounter(c));
|
||||
});
|
||||
|
||||
if (this.state.daypart !== false) {
|
||||
counters.push(this.renderDayPart());
|
||||
}
|
||||
|
||||
if (
|
||||
this.state.counters.length === 3 &&
|
||||
this.props.timeFormat.indexOf('S') !== -1
|
||||
) {
|
||||
counters.push(
|
||||
<div className={cx('CalendarCounter-sep')} key="sep5">
|
||||
:
|
||||
</div>
|
||||
);
|
||||
counters.push(
|
||||
<div className={cx('CalendarCounter CalendarCounter--milli')}>
|
||||
<input
|
||||
value={this.state.milliseconds}
|
||||
type="text"
|
||||
onChange={this.updateMilli}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <div className={cx('CalendarTime')}>{counters}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default localeable(CustomTimeView as any);
|
@ -154,7 +154,7 @@ export interface ChartProps extends RendererProps, Omit<ChartSchema, 'type'> {
|
||||
export class Chart extends React.Component<ChartProps> {
|
||||
static defaultProps: Partial<ChartProps> = {
|
||||
replaceChartOption: false,
|
||||
unMountOnHidden: true
|
||||
unMountOnHidden: false
|
||||
};
|
||||
|
||||
static propsList: Array<string> = [];
|
||||
|
@ -10,7 +10,7 @@ export interface DateBaseControlSchema extends FormBaseControl {
|
||||
/**
|
||||
* 指定为日期选择控件
|
||||
*/
|
||||
type: 'date' | 'datetime' | 'time' | 'month';
|
||||
type: 'date' | 'datetime' | 'time' | 'month' | 'quarter';
|
||||
|
||||
/**
|
||||
* 是否显示清除按钮
|
||||
@ -179,11 +179,49 @@ export interface MonthControlSchema extends DateBaseControlSchema {
|
||||
inputFormat?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 季度选择控件
|
||||
*/
|
||||
export interface QuarterControlSchema extends DateBaseControlSchema {
|
||||
/**
|
||||
* 指定为月份时间选择控件
|
||||
*/
|
||||
type: 'quarter';
|
||||
|
||||
/**
|
||||
* 月份存储格式
|
||||
* @default X
|
||||
*/
|
||||
format?: string;
|
||||
|
||||
/**
|
||||
* 月份展示格式
|
||||
* @default YYYY-MM
|
||||
*/
|
||||
inputFormat?: string;
|
||||
}
|
||||
|
||||
export interface DateProps extends FormControlProps {
|
||||
inputFormat?: string;
|
||||
timeFormat?: string;
|
||||
format?: string;
|
||||
timeConstraints?: object;
|
||||
timeConstraints?: {
|
||||
hours?: {
|
||||
min: number;
|
||||
max: number;
|
||||
step: number;
|
||||
};
|
||||
minutes?: {
|
||||
min: number;
|
||||
max: number;
|
||||
step: number;
|
||||
};
|
||||
seconds: {
|
||||
min: number;
|
||||
max: number;
|
||||
step: number;
|
||||
};
|
||||
};
|
||||
closeOnSelect?: boolean;
|
||||
disabled: boolean;
|
||||
iconClassName?: string;
|
||||
@ -267,19 +305,32 @@ export default class DateControl extends React.PureComponent<
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
let {
|
||||
className,
|
||||
defaultValue,
|
||||
defaultData,
|
||||
classnames: cx,
|
||||
minDate,
|
||||
maxDate,
|
||||
type,
|
||||
format,
|
||||
timeFormat,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
if (type === 'time' && timeFormat) {
|
||||
format = timeFormat;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cx(`DateControl`, className)}>
|
||||
<DatePicker {...rest} {...this.state} classnames={cx} />
|
||||
<DatePicker
|
||||
{...rest}
|
||||
timeFormat={timeFormat}
|
||||
format={format}
|
||||
{...this.state}
|
||||
classnames={cx}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -344,6 +395,21 @@ export class MonthControlRenderer extends DateControl {
|
||||
};
|
||||
}
|
||||
|
||||
@FormItem({
|
||||
type: 'quarter'
|
||||
})
|
||||
export class QuarterControlRenderer extends DateControl {
|
||||
static defaultProps = {
|
||||
...DateControl.defaultProps,
|
||||
placeholder: '请选择季度',
|
||||
inputFormat: 'YYYY [Q]Q',
|
||||
dateFormat: 'YYYY [Q]Q',
|
||||
timeFormat: '',
|
||||
viewMode: 'quarters',
|
||||
closeOnSelect: true
|
||||
};
|
||||
}
|
||||
|
||||
@FormItem({
|
||||
type: 'year'
|
||||
})
|
||||
|
@ -36,6 +36,7 @@ import {
|
||||
DateControlSchema,
|
||||
DateTimeControlSchema,
|
||||
MonthControlSchema,
|
||||
QuarterControlSchema,
|
||||
TimeControlSchema
|
||||
} from './Date';
|
||||
import {DateRangeControlSchema} from './DateRange';
|
||||
@ -98,6 +99,7 @@ export type FormControlType =
|
||||
| 'date'
|
||||
| 'datetime'
|
||||
| 'time'
|
||||
| 'quarter'
|
||||
| 'month'
|
||||
| 'date-range'
|
||||
| 'diff'
|
||||
@ -210,6 +212,8 @@ export type FormControlSchema =
|
||||
| DateTimeControlSchema
|
||||
| TimeControlSchema
|
||||
| MonthControlSchema
|
||||
| MonthControlSchema
|
||||
| QuarterControlSchema
|
||||
| DateRangeControlSchema
|
||||
| DiffControlSchema
|
||||
| EditorControlSchema
|
||||
|
@ -123,6 +123,13 @@ export interface ThemeProps {
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
export interface ThemeOutterProps {
|
||||
theme?: string;
|
||||
className?: string;
|
||||
classPrefix?: string;
|
||||
classnames?: ClassNamesFn;
|
||||
}
|
||||
|
||||
export let defaultTheme: string = 'default';
|
||||
export const ThemeContext = React.createContext('');
|
||||
|
||||
@ -134,12 +141,8 @@ export function themeable<
|
||||
type OuterProps = JSX.LibraryManagedAttributes<
|
||||
T,
|
||||
Omit<React.ComponentProps<T>, keyof ThemeProps>
|
||||
> & {
|
||||
theme?: string;
|
||||
className?: string;
|
||||
classPrefix?: string;
|
||||
classnames?: ClassNamesFn;
|
||||
};
|
||||
> &
|
||||
ThemeOutterProps;
|
||||
|
||||
const result = hoistNonReactStatic(
|
||||
class extends React.Component<OuterProps> {
|
||||
|
Loading…
Reference in New Issue
Block a user