Merged PR 737859: Support Input.Time

Support Input.Time
This commit is contained in:
Dongyu Zhao 2018-08-17 10:11:36 +00:00
Родитель 0f2a21c2ca
Коммит dfdee71720
9 изменённых файлов: 727 добавлений и 0 удалений

85
dist/Components/Inputs/TimePanel.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,85 @@
import * as React from 'react';
import { DatePickerIOS, Platform, TimePickerAndroid } from 'react-native';
import { StyleManager } from '../../Styles/StyleManager';
import { TimeUtils } from '../../Utils/TimeUtils';
import { ButtonGroup } from '../Containers/ButtonGroup';
import { Card } from '../Containers/Card';
import { ModalBox } from '../Containers/ModalBox';
import { Button } from './Button';
export class TimePanel extends React.Component {
constructor() {
super(...arguments);
this.onCancel = () => {
if (this.props.onCancel) {
this.props.onCancel();
}
};
this.onSave = () => {
if (this.props.onSave) {
this.props.onSave();
}
};
this.onTimeChangeIos = (date) => {
if (this.props.onValueChange) {
this.props.onValueChange(TimeUtils.getTimeString(date));
}
};
this.onTimeChange = (hour, minute) => {
if (this.props.onValueChange) {
this.props.onValueChange(TimeUtils.composeTimeString(hour, minute));
}
};
}
componentDidUpdate(prevProps) {
if (Platform.OS === 'android' && this.props.show && !prevProps.show) {
this.showPickerAndroid();
}
}
render() {
if (Platform.OS === 'ios') {
return (React.createElement(ModalBox, { show: this.show },
React.createElement(Card, { flex: 0, fit: 'content' },
React.createElement(DatePickerIOS, { date: TimeUtils.extractTime(this.props.value), mode: 'time', onDateChange: this.onTimeChangeIos }),
React.createElement(ButtonGroup, null,
this.renderCancelButton(),
this.renderSaveButton()))));
}
return null;
}
renderCancelButton() {
return (React.createElement(Button, { flex: 1, title: 'Cancel', color: StyleManager.getColor('accent', 'default', false), fontSize: StyleManager.getFontSize('default'), fontWeight: StyleManager.getFontWeight('bolder'), backgroundColor: StyleManager.getBackgroundColor('default'), textHorizontalAlign: 'center', textVerticalAlign: 'center', paddingTop: 6, paddingBottom: 6, paddingLeft: 16, paddingRight: 16, onPress: this.onCancel }));
}
renderSaveButton() {
return (React.createElement(Button, { flex: 1, title: 'Save', color: StyleManager.getColor('accent', 'default', false), fontSize: StyleManager.getFontSize('default'), fontWeight: StyleManager.getFontWeight('bolder'), backgroundColor: StyleManager.getBackgroundColor('default'), textHorizontalAlign: 'center', textVerticalAlign: 'center', paddingTop: 6, paddingBottom: 6, paddingLeft: 16, paddingRight: 16, onPress: this.onSave, style: {
borderLeftWidth: 1,
borderLeftColor: StyleManager.separatorColor,
} }));
}
async showPickerAndroid() {
if (Platform.OS === 'android') {
const now = TimeUtils.extractTime(this.props.value);
try {
const { action, hour, minute } = await TimePickerAndroid.open({
hour: now.getHours(),
minute: now.getMinutes(),
is24Hour: true
});
if (action === TimePickerAndroid.timeSetAction) {
this.onTimeChange(hour, minute);
this.onSave();
}
if (action === TimePickerAndroid.dismissedAction) {
this.setState({
showTimePicker: false
}, this.onCancel);
}
}
catch ({ code, message }) {
console.warn('Cannot open time picker', message);
}
}
}
get show() {
return this.props.show && Platform.OS === 'ios';
}
}

3
dist/Views/Factories/ContentFactory.js поставляемый
Просмотреть файл

@ -12,6 +12,7 @@ import { ImageSetView } from '../Containers/ImageSet';
import { DateInputView } from '../Inputs/DateInput';
import { NumberInputView } from '../Inputs/NumberInput';
import { TextInputView } from '../Inputs/TextInput';
import { TimeInputView } from '../Inputs/TimeInput';
export class ContentFactory {
static createView(element, index, theme) {
if (element) {
@ -58,6 +59,8 @@ export class ContentFactory {
return (React.createElement(NumberInputView, { key: 'NumberInput' + index, element: element, index: index, theme: theme }));
case ContentElementType.DateInput:
return (React.createElement(DateInputView, { key: 'DateInputView' + index, element: element, index: index, theme: theme }));
case ContentElementType.TimeInput:
return (React.createElement(TimeInputView, { key: 'TimeInputView' + index, element: element, index: index, theme: theme }));
default:
return null;
}

110
dist/Views/Inputs/TimeInput.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,110 @@
import * as React from 'react';
import { Button } from '../../Components/Inputs/Button';
import { TimePanel } from '../../Components/Inputs/TimePanel';
import { FormContext } from '../../Contexts/FormContext';
import { StyleManager } from '../../Styles/StyleManager';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
export class TimeInputView extends React.Component {
constructor(props) {
super(props);
this.tempValue = '';
this.onValueChange = (value) => {
this.tempValue = value;
};
this.onCancel = () => {
this.setState({
focused: false,
}, () => {
this.tempValue = this.state.value;
});
};
this.onSave = () => {
this.setState({
value: this.tempValue,
focused: false,
}, () => {
this.updateStore();
});
};
this.validateInput = (input) => {
if (this.props.element) {
return this.props.element.validate(input);
}
return true;
};
this.onPress = () => {
this.setState({
focused: !this.state.focused,
});
console.log('on press');
};
const { element } = this.props;
if (element && element.isValid) {
this.state = {
focused: false,
value: element.value
};
}
}
render() {
const { element, index, theme } = this.props;
if (!element || !element.isValid) {
return DebugOutputFactory.createDebugOutputBanner(element.type + '>>' + element.id + ' is not valid', theme, 'error');
}
return ([
React.createElement(Button, { key: 'TimeInputButton' + index, title: this.state.value, color: this.color, backgroundColor: this.backgroundColor, borderColor: this.borderColor, borderRadius: 4, borderWidth: 1, height: this.height, fontSize: this.fontSize, fontWeight: this.fontWeight, textHorizontalAlign: 'center', textVerticalAlign: 'center', marginTop: this.spacing, paddingLeft: this.paddingHorizontal, paddingRight: this.paddingHorizontal, paddingTop: this.paddingVertical, paddingBottom: this.paddingVertical, onPress: this.onPress }),
React.createElement(TimePanel, { key: 'TimePanel' + index, value: this.state.value, show: this.state.focused, onValueChange: this.onValueChange, onSave: this.onSave, onCancel: this.onCancel })
]);
}
updateStore() {
FormContext.getInstance().updateField(this.props.element.id, this.state.value, this.validateInput(this.state.value));
}
get fontSize() {
return StyleManager.getFontSize('default');
}
get fontWeight() {
return StyleManager.getFontWeight('default');
}
get paddingVertical() {
return 12;
}
get paddingHorizontal() {
return 12;
}
get numberOfLine() {
return 1;
}
get height() {
return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
}
get color() {
if (this.state.focused) {
return StyleManager.getInputFocusColor(this.props.theme);
}
else {
return StyleManager.getInputColor(this.props.theme);
}
}
get backgroundColor() {
if (this.state.focused) {
return StyleManager.getInputFocusBackgroundColor(this.props.theme);
}
else {
return StyleManager.getInputBackgroundColor(this.props.theme);
}
}
get borderColor() {
if (this.state.focused) {
return StyleManager.getInputFocusBorderColor(this.props.theme);
}
else {
return StyleManager.getInputBorderColor(this.props.theme);
}
}
get spacing() {
if (this.props.index !== undefined && this.props.index > 0) {
return StyleManager.getSpacing(this.props.element.spacing);
}
return 0;
}
}

Просмотреть файл

@ -0,0 +1,85 @@
import * as React from 'react';
import { DatePickerIOS, Platform, TimePickerAndroid } from 'react-native';
import { StyleManager } from '../../Styles/StyleManager';
import { TimeUtils } from '../../Utils/TimeUtils';
import { ButtonGroup } from '../Containers/ButtonGroup';
import { Card } from '../Containers/Card';
import { ModalBox } from '../Containers/ModalBox';
import { Button } from './Button';
export class TimePanel extends React.Component {
constructor() {
super(...arguments);
this.onCancel = () => {
if (this.props.onCancel) {
this.props.onCancel();
}
};
this.onSave = () => {
if (this.props.onSave) {
this.props.onSave();
}
};
this.onTimeChangeIos = (date) => {
if (this.props.onValueChange) {
this.props.onValueChange(TimeUtils.getTimeString(date));
}
};
this.onTimeChange = (hour, minute) => {
if (this.props.onValueChange) {
this.props.onValueChange(TimeUtils.composeTimeString(hour, minute));
}
};
}
componentDidUpdate(prevProps) {
if (Platform.OS === 'android' && this.props.show && !prevProps.show) {
this.showPickerAndroid();
}
}
render() {
if (Platform.OS === 'ios') {
return (React.createElement(ModalBox, { show: this.show },
React.createElement(Card, { flex: 0, fit: 'content' },
React.createElement(DatePickerIOS, { date: TimeUtils.extractTime(this.props.value), mode: 'time', onDateChange: this.onTimeChangeIos }),
React.createElement(ButtonGroup, null,
this.renderCancelButton(),
this.renderSaveButton()))));
}
return null;
}
renderCancelButton() {
return (React.createElement(Button, { flex: 1, title: 'Cancel', color: StyleManager.getColor('accent', 'default', false), fontSize: StyleManager.getFontSize('default'), fontWeight: StyleManager.getFontWeight('bolder'), backgroundColor: StyleManager.getBackgroundColor('default'), textHorizontalAlign: 'center', textVerticalAlign: 'center', paddingTop: 6, paddingBottom: 6, paddingLeft: 16, paddingRight: 16, onPress: this.onCancel }));
}
renderSaveButton() {
return (React.createElement(Button, { flex: 1, title: 'Save', color: StyleManager.getColor('accent', 'default', false), fontSize: StyleManager.getFontSize('default'), fontWeight: StyleManager.getFontWeight('bolder'), backgroundColor: StyleManager.getBackgroundColor('default'), textHorizontalAlign: 'center', textVerticalAlign: 'center', paddingTop: 6, paddingBottom: 6, paddingLeft: 16, paddingRight: 16, onPress: this.onSave, style: {
borderLeftWidth: 1,
borderLeftColor: StyleManager.separatorColor,
} }));
}
async showPickerAndroid() {
if (Platform.OS === 'android') {
const now = TimeUtils.extractTime(this.props.value);
try {
const { action, hour, minute } = await TimePickerAndroid.open({
hour: now.getHours(),
minute: now.getMinutes(),
is24Hour: true
});
if (action === TimePickerAndroid.timeSetAction) {
this.onTimeChange(hour, minute);
this.onSave();
}
if (action === TimePickerAndroid.dismissedAction) {
this.setState({
showTimePicker: false
}, this.onCancel);
}
}
catch ({ code, message }) {
console.warn('Cannot open time picker', message);
}
}
}
get show() {
return this.props.show && Platform.OS === 'ios';
}
}

Просмотреть файл

@ -12,6 +12,7 @@ import { ImageSetView } from '../Containers/ImageSet';
import { DateInputView } from '../Inputs/DateInput';
import { NumberInputView } from '../Inputs/NumberInput';
import { TextInputView } from '../Inputs/TextInput';
import { TimeInputView } from '../Inputs/TimeInput';
export class ContentFactory {
static createView(element, index, theme) {
if (element) {
@ -58,6 +59,8 @@ export class ContentFactory {
return (React.createElement(NumberInputView, { key: 'NumberInput' + index, element: element, index: index, theme: theme }));
case ContentElementType.DateInput:
return (React.createElement(DateInputView, { key: 'DateInputView' + index, element: element, index: index, theme: theme }));
case ContentElementType.TimeInput:
return (React.createElement(TimeInputView, { key: 'TimeInputView' + index, element: element, index: index, theme: theme }));
default:
return null;
}

Просмотреть файл

@ -0,0 +1,110 @@
import * as React from 'react';
import { Button } from '../../Components/Inputs/Button';
import { TimePanel } from '../../Components/Inputs/TimePanel';
import { FormContext } from '../../Contexts/FormContext';
import { StyleManager } from '../../Styles/StyleManager';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
export class TimeInputView extends React.Component {
constructor(props) {
super(props);
this.tempValue = '';
this.onValueChange = (value) => {
this.tempValue = value;
};
this.onCancel = () => {
this.setState({
focused: false,
}, () => {
this.tempValue = this.state.value;
});
};
this.onSave = () => {
this.setState({
value: this.tempValue,
focused: false,
}, () => {
this.updateStore();
});
};
this.validateInput = (input) => {
if (this.props.element) {
return this.props.element.validate(input);
}
return true;
};
this.onPress = () => {
this.setState({
focused: !this.state.focused,
});
console.log('on press');
};
const { element } = this.props;
if (element && element.isValid) {
this.state = {
focused: false,
value: element.value
};
}
}
render() {
const { element, index, theme } = this.props;
if (!element || !element.isValid) {
return DebugOutputFactory.createDebugOutputBanner(element.type + '>>' + element.id + ' is not valid', theme, 'error');
}
return ([
React.createElement(Button, { key: 'TimeInputButton' + index, title: this.state.value, color: this.color, backgroundColor: this.backgroundColor, borderColor: this.borderColor, borderRadius: 4, borderWidth: 1, height: this.height, fontSize: this.fontSize, fontWeight: this.fontWeight, textHorizontalAlign: 'center', textVerticalAlign: 'center', marginTop: this.spacing, paddingLeft: this.paddingHorizontal, paddingRight: this.paddingHorizontal, paddingTop: this.paddingVertical, paddingBottom: this.paddingVertical, onPress: this.onPress }),
React.createElement(TimePanel, { key: 'TimePanel' + index, value: this.state.value, show: this.state.focused, onValueChange: this.onValueChange, onSave: this.onSave, onCancel: this.onCancel })
]);
}
updateStore() {
FormContext.getInstance().updateField(this.props.element.id, this.state.value, this.validateInput(this.state.value));
}
get fontSize() {
return StyleManager.getFontSize('default');
}
get fontWeight() {
return StyleManager.getFontWeight('default');
}
get paddingVertical() {
return 12;
}
get paddingHorizontal() {
return 12;
}
get numberOfLine() {
return 1;
}
get height() {
return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
}
get color() {
if (this.state.focused) {
return StyleManager.getInputFocusColor(this.props.theme);
}
else {
return StyleManager.getInputColor(this.props.theme);
}
}
get backgroundColor() {
if (this.state.focused) {
return StyleManager.getInputFocusBackgroundColor(this.props.theme);
}
else {
return StyleManager.getInputBackgroundColor(this.props.theme);
}
}
get borderColor() {
if (this.state.focused) {
return StyleManager.getInputFocusBorderColor(this.props.theme);
}
else {
return StyleManager.getInputBorderColor(this.props.theme);
}
}
get spacing() {
if (this.props.index !== undefined && this.props.index > 0) {
return StyleManager.getSpacing(this.props.element.spacing);
}
return 0;
}
}

Просмотреть файл

@ -0,0 +1,147 @@
import * as React from 'react';
import { DatePickerIOS, Platform, TimePickerAndroid } from 'react-native';
import { StyleManager } from '../../Styles/StyleManager';
import { TimeUtils } from '../../Utils/TimeUtils';
import { ButtonGroup } from '../Containers/ButtonGroup';
import { Card } from '../Containers/Card';
import { ModalBox } from '../Containers/ModalBox';
import { Button } from './Button';
interface IProps {
value: string;
show: boolean;
onValueChange: (value: string) => void;
onSave: () => void;
onCancel: () => void;
}
export class TimePanel extends React.Component<IProps> {
public componentDidUpdate(prevProps: IProps) {
if (Platform.OS === 'android' && this.props.show && !prevProps.show) {
this.showPickerAndroid();
}
}
public render() {
if (Platform.OS === 'ios') {
return (
<ModalBox
show={this.show}
>
<Card
flex={0}
fit='content'
>
<DatePickerIOS
date={TimeUtils.extractTime(this.props.value)}
mode='time'
onDateChange={this.onTimeChangeIos}
/>
<ButtonGroup>
{this.renderCancelButton()}
{this.renderSaveButton()}
</ButtonGroup>
</Card>
</ModalBox>
);
}
return null;
}
private renderCancelButton() {
return (
<Button
flex={1}
title='Cancel'
color={StyleManager.getColor('accent', 'default', false)}
fontSize={StyleManager.getFontSize('default')}
fontWeight={StyleManager.getFontWeight('bolder')}
backgroundColor={StyleManager.getBackgroundColor('default')}
textHorizontalAlign='center'
textVerticalAlign='center'
paddingTop={6}
paddingBottom={6}
paddingLeft={16}
paddingRight={16}
onPress={this.onCancel}
/>
);
}
private renderSaveButton() {
return (
<Button
flex={1}
title='Save'
color={StyleManager.getColor('accent', 'default', false)}
fontSize={StyleManager.getFontSize('default')}
fontWeight={StyleManager.getFontWeight('bolder')}
backgroundColor={StyleManager.getBackgroundColor('default')}
textHorizontalAlign='center'
textVerticalAlign='center'
paddingTop={6}
paddingBottom={6}
paddingLeft={16}
paddingRight={16}
onPress={this.onSave}
style={{
borderLeftWidth: 1,
borderLeftColor: StyleManager.separatorColor,
}}
/>
);
}
private async showPickerAndroid() {
if (Platform.OS === 'android') {
const now = TimeUtils.extractTime(this.props.value);
try {
const { action, hour, minute } = await TimePickerAndroid.open({
hour: now.getHours(),
minute: now.getMinutes(),
is24Hour: true
});
if (action === TimePickerAndroid.timeSetAction) {
this.onTimeChange(hour, minute);
this.onSave();
}
if (action === TimePickerAndroid.dismissedAction) {
this.setState({
showTimePicker: false
}, this.onCancel);
}
} catch ({ code, message }) {
console.warn('Cannot open time picker', message);
}
}
}
private onCancel = () => {
if (this.props.onCancel) {
this.props.onCancel();
}
}
private onSave = () => {
if (this.props.onSave) {
this.props.onSave();
}
}
private onTimeChangeIos = (date: Date) => {
if (this.props.onValueChange) {
this.props.onValueChange(TimeUtils.getTimeString(date));
}
}
private onTimeChange = (hour: number, minute: number) => {
if (this.props.onValueChange) {
this.props.onValueChange(TimeUtils.composeTimeString(hour, minute));
}
}
private get show() {
return this.props.show && Platform.OS === 'ios';
}
}

Просмотреть файл

@ -12,6 +12,7 @@ import { ImageSetElement } from '../../Schema/Containers/ImageSet';
import { DateInputElement } from '../../Schema/Inputs/DateInput';
import { NumberInputElement } from '../../Schema/Inputs/NumberInput';
import { TextInputElement } from '../../Schema/Inputs/TextInput';
import { TimeInputElement } from '../../Schema/Inputs/TimeInput';
import { ImageView } from '../CardElements/Image';
import { TextBlockView } from '../CardElements/TextBlock';
import { AdaptiveCardView } from '../Cards/AdaptiveCard';
@ -22,6 +23,7 @@ import { ImageSetView } from '../Containers/ImageSet';
import { DateInputView } from '../Inputs/DateInput';
import { NumberInputView } from '../Inputs/NumberInput';
import { TextInputView } from '../Inputs/TextInput';
import { TimeInputView } from '../Inputs/TimeInput';
export class ContentFactory {
public static createView(element: ContentElement, index: number, theme: 'default' | 'emphasis'): JSX.Element[] {
@ -149,6 +151,15 @@ export class ContentFactory {
theme={theme}
/>
);
case ContentElementType.TimeInput:
return (
<TimeInputView
key={'TimeInputView' + index}
element={element as TimeInputElement}
index={index}
theme={theme}
/>
);
default:
return null;
}

Просмотреть файл

@ -0,0 +1,173 @@
import * as React from 'react';
import { Button } from '../../Components/Inputs/Button';
import { TimePanel } from '../../Components/Inputs/TimePanel';
import { FormContext } from '../../Contexts/FormContext';
import { TimeInputElement } from '../../Schema/Inputs/TimeInput';
import { StyleManager } from '../../Styles/StyleManager';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
interface IProps {
index: number;
element: TimeInputElement;
theme: 'default' | 'emphasis';
}
interface IState {
value: string;
focused: boolean;
}
export class TimeInputView extends React.Component<IProps, IState> {
private tempValue = '';
constructor(props: IProps) {
super(props);
const { element } = this.props;
if (element && element.isValid) {
this.state = {
focused: false,
value: element.value
};
}
}
public render() {
const { element, index, theme } = this.props;
if (!element || !element.isValid) {
return DebugOutputFactory.createDebugOutputBanner(element.type + '>>' + element.id + ' is not valid', theme, 'error');
}
return (
[
<Button
key={'TimeInputButton' + index}
title={this.state.value}
color={this.color}
backgroundColor={this.backgroundColor}
borderColor={this.borderColor}
borderRadius={4}
borderWidth={1}
height={this.height}
fontSize={this.fontSize}
fontWeight={this.fontWeight}
textHorizontalAlign='center'
textVerticalAlign='center'
marginTop={this.spacing}
paddingLeft={this.paddingHorizontal}
paddingRight={this.paddingHorizontal}
paddingTop={this.paddingVertical}
paddingBottom={this.paddingVertical}
onPress={this.onPress}
/>,
<TimePanel
key={'TimePanel' + index}
value={this.state.value}
show={this.state.focused}
onValueChange={this.onValueChange}
onSave={this.onSave}
onCancel={this.onCancel}
/>
]
);
}
private onValueChange = (value: string) => {
this.tempValue = value;
}
private onCancel = () => {
this.setState({
focused: false,
}, () => {
this.tempValue = this.state.value;
});
}
private onSave = () => {
this.setState({
value: this.tempValue,
focused: false,
}, () => {
this.updateStore();
});
}
private validateInput = (input: string) => {
if (this.props.element) {
return this.props.element.validate(input);
}
return true;
}
private updateStore() {
FormContext.getInstance().updateField(
this.props.element.id,
this.state.value,
this.validateInput(this.state.value)
);
}
private onPress = () => {
this.setState({
focused: !this.state.focused,
});
console.log('on press');
}
private get fontSize() {
return StyleManager.getFontSize('default');
}
private get fontWeight() {
return StyleManager.getFontWeight('default');
}
private get paddingVertical() {
return 12;
}
private get paddingHorizontal() {
return 12;
}
private get numberOfLine() {
return 1;
}
private get height() {
return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
}
private get color() {
if (this.state.focused) {
return StyleManager.getInputFocusColor(this.props.theme);
} else {
return StyleManager.getInputColor(this.props.theme);
}
}
private get backgroundColor() {
if (this.state.focused) {
return StyleManager.getInputFocusBackgroundColor(this.props.theme);
} else {
return StyleManager.getInputBackgroundColor(this.props.theme);
}
}
private get borderColor() {
if (this.state.focused) {
return StyleManager.getInputFocusBorderColor(this.props.theme);
} else {
return StyleManager.getInputBorderColor(this.props.theme);
}
}
private get spacing() {
if (this.props.index !== undefined && this.props.index > 0) {
return StyleManager.getSpacing(this.props.element.spacing);
}
return 0;
}
}