Merged PR 740936: Support PeoplePicker
Support PeoplePicker
This commit is contained in:
Родитель
dfdee71720
Коммит
b5ec41e080
|
@ -13,28 +13,26 @@ import { Guid } from '../../Shared/Guid';
|
||||||
export class Touchable extends React.Component {
|
export class Touchable extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
this.testId = this.props.testId + Guid.newGuid();
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
DeviceEventEmitter.addListener('KeyEnter' + this.props.testId, this.props.onPress);
|
DeviceEventEmitter.addListener('KeyEnter' + this.testId, this.props.onPress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
DeviceEventEmitter.removeListener('KeyEnter' + this.props.testId, this.props.onPress);
|
DeviceEventEmitter.removeListener('KeyEnter' + this.testId, this.props.onPress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
const _a = this.props, { onPress, onLongPress, disabled, accessibilityLabel, accessibilityTraits, accessibilityComponentType, activeOpacity, hitSlop, style } = _a, otherProps = __rest(_a, ["onPress", "onLongPress", "disabled", "accessibilityLabel", "accessibilityTraits", "accessibilityComponentType", "activeOpacity", "hitSlop", "style"]);
|
const _a = this.props, { onPress, onLongPress, disabled, accessibilityLabel, accessibilityTraits, accessibilityComponentType, activeOpacity, hitSlop, style } = _a, otherProps = __rest(_a, ["onPress", "onLongPress", "disabled", "accessibilityLabel", "accessibilityTraits", "accessibilityComponentType", "activeOpacity", "hitSlop", "style"]);
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
return (React.createElement(TouchableNativeFeedback, { disabled: disabled, onPress: onPress, onLongPress: onLongPress, accessible: true, testID: this.uniqueTestId, useForeground: true, hitSlop: hitSlop, background: TouchableNativeFeedback.SelectableBackground(), accessibilityLabel: accessibilityLabel },
|
return (React.createElement(TouchableNativeFeedback, { disabled: disabled, onPress: onPress, onLongPress: onLongPress, accessible: true, testID: this.testId, useForeground: true, hitSlop: hitSlop, background: TouchableNativeFeedback.SelectableBackground(), accessibilityLabel: accessibilityLabel },
|
||||||
React.createElement(View, Object.assign({ style: style, onLayout: this.props.onLayout }, otherProps))));
|
React.createElement(View, Object.assign({ style: style, onLayout: this.props.onLayout }, otherProps))));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return (React.createElement(TouchableOpacity, { disabled: disabled, onPress: onPress, onLongPress: onLongPress, accessible: true, testID: this.uniqueTestId, activeOpacity: activeOpacity, style: style, hitSlop: hitSlop, accessibilityLabel: accessibilityLabel, onLayout: this.props.onLayout }, otherProps.children));
|
return (React.createElement(TouchableOpacity, { disabled: disabled, onPress: onPress, onLongPress: onLongPress, accessible: true, testID: this.testId, activeOpacity: activeOpacity, style: style, hitSlop: hitSlop, accessibilityLabel: accessibilityLabel, onLayout: this.props.onLayout }, otherProps.children));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get uniqueTestId() {
|
|
||||||
return this.props.testId + Guid.newGuid();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Text, View } from 'react-native';
|
||||||
|
import { StyleManager } from '../../Styles/StyleManager';
|
||||||
|
import { ImageBlock } from '../Basic/ImageBlock';
|
||||||
|
import { Touchable } from '../Basic/Touchable';
|
||||||
|
export class Contact extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
this.onPress = () => {
|
||||||
|
if (this.props.onSelect) {
|
||||||
|
this.props.onSelect(this.props.hiddenFields);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
if (this.props.onSelect) {
|
||||||
|
return this.renderTouchableBlock();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return this.renderNonTouchableBlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
renderTouchableBlock() {
|
||||||
|
return (React.createElement(Touchable, { onPress: this.onPress, style: {
|
||||||
|
alignSelf: 'stretch',
|
||||||
|
flexDirection: 'row'
|
||||||
|
} }, this.renderContent()));
|
||||||
|
}
|
||||||
|
renderNonTouchableBlock() {
|
||||||
|
return (React.createElement(View, { alignSelf: 'stretch', flexDirection: 'row' }, this.renderContent()));
|
||||||
|
}
|
||||||
|
renderContent() {
|
||||||
|
return [
|
||||||
|
React.createElement(ImageBlock, { url: this.props.avatar, mode: 'avatar', width: StyleManager.getImageSize('medium'), height: StyleManager.getImageSize('medium') }),
|
||||||
|
React.createElement(View, null,
|
||||||
|
React.createElement(Text, { accessible: true, style: {
|
||||||
|
color: StyleManager.getColor('default', this.props.theme, false),
|
||||||
|
fontSize: StyleManager.getFontSize('default'),
|
||||||
|
fontWeight: StyleManager.getFontWeight('default'),
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
textAlign: StyleManager.getTextAlign('left'),
|
||||||
|
flexWrap: StyleManager.getWrap(true),
|
||||||
|
} }, this.props.mainInfo),
|
||||||
|
React.createElement(Text, { accessible: true, style: {
|
||||||
|
color: StyleManager.getColor('default', this.props.theme, true),
|
||||||
|
fontSize: StyleManager.getFontSize('small'),
|
||||||
|
fontWeight: StyleManager.getFontWeight('default'),
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
textAlign: StyleManager.getTextAlign('left'),
|
||||||
|
flexWrap: StyleManager.getWrap(true),
|
||||||
|
} }, this.props.subInfo))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -87,7 +87,7 @@ export class InputBox extends React.Component {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
get height() {
|
get height() {
|
||||||
return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
|
return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
|
||||||
}
|
}
|
||||||
get color() {
|
get color() {
|
||||||
if (this.state.focused) {
|
if (this.state.focused) {
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { ScrollView, Text, TextInput, View } from 'react-native';
|
||||||
|
import { StyleManager } from '../../Styles/StyleManager';
|
||||||
|
import { SeparateLine } from '../Basic/SeparateLine';
|
||||||
|
export class LabelInput extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.onValueChange = (value) => {
|
||||||
|
if (this.props.onRequestSuggestion) {
|
||||||
|
this.props.onRequestSuggestion(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.onBlur = () => {
|
||||||
|
this.setState({ focused: false }, () => {
|
||||||
|
this.validateInput();
|
||||||
|
if (this.props.onBlur) {
|
||||||
|
this.props.onBlur();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.onFocus = () => {
|
||||||
|
this.setState({
|
||||||
|
focused: true
|
||||||
|
}, () => {
|
||||||
|
if (this.props.onFocus) {
|
||||||
|
this.props.onFocus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.state = {
|
||||||
|
focused: this.props.focused,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (!prevState.focused && this.props.focused) {
|
||||||
|
this.setState({
|
||||||
|
focused: true,
|
||||||
|
}, () => {
|
||||||
|
if (this.inputBox) {
|
||||||
|
this.inputBox.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return (React.createElement(View, { style: {
|
||||||
|
flex: this.props.flex,
|
||||||
|
} },
|
||||||
|
this.renderInputArea(),
|
||||||
|
this.renderSuggestions()));
|
||||||
|
}
|
||||||
|
renderInputArea() {
|
||||||
|
return (React.createElement(View, { style: {
|
||||||
|
alignSelf: 'stretch',
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
backgroundColor: this.backgroundColor,
|
||||||
|
borderColor: this.borderColor,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: 4,
|
||||||
|
width: this.props.width,
|
||||||
|
marginTop: this.props.marginTop,
|
||||||
|
marginRight: this.props.marginRight,
|
||||||
|
marginBottom: this.props.marginBottom,
|
||||||
|
marginLeft: this.props.marginLeft,
|
||||||
|
} },
|
||||||
|
this.renderLabels(),
|
||||||
|
this.renderInputBox()));
|
||||||
|
}
|
||||||
|
renderLabels() {
|
||||||
|
if (this.props.labels) {
|
||||||
|
return this.props.labels.map((label, index) => {
|
||||||
|
return (React.createElement(Text, { key: 'Label' + index, style: {
|
||||||
|
fontSize: this.fontSize,
|
||||||
|
fontWeight: this.fontWeight,
|
||||||
|
color: this.backgroundColor,
|
||||||
|
backgroundColor: this.color,
|
||||||
|
paddingTop: this.paddingVertical - 6,
|
||||||
|
paddingBottom: this.paddingVertical - 6,
|
||||||
|
borderRadius: 4,
|
||||||
|
paddingLeft: 6,
|
||||||
|
paddingRight: 6,
|
||||||
|
marginTop: 6,
|
||||||
|
marginBottom: 6,
|
||||||
|
marginLeft: 6,
|
||||||
|
} }, label.title));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
renderInputBox() {
|
||||||
|
return (React.createElement(TextInput, { ref: ref => this.inputBox = ref, style: [
|
||||||
|
{
|
||||||
|
flex: 1,
|
||||||
|
color: this.color,
|
||||||
|
fontSize: this.fontSize,
|
||||||
|
fontWeight: this.fontWeight,
|
||||||
|
paddingTop: this.paddingVertical,
|
||||||
|
paddingRight: this.paddingHorizontal,
|
||||||
|
paddingBottom: this.paddingVertical,
|
||||||
|
paddingLeft: this.paddingHorizontal,
|
||||||
|
},
|
||||||
|
this.props.style
|
||||||
|
], multiline: this.isMultiLine, numberOfLines: this.props.numberOfLines, keyboardType: this.props.keyboardType, blurOnSubmit: !this.isMultiLine, placeholder: this.props.placeholder, value: this.props.value, returnKeyType: this.props.returnKeyType, underlineColorAndroid: 'transparent', importantForAccessibility: 'no-hide-descendants', onChangeText: this.onValueChange, onFocus: this.onFocus, onBlur: this.onBlur }));
|
||||||
|
}
|
||||||
|
renderSuggestions() {
|
||||||
|
if (this.props.suggestionView) {
|
||||||
|
return [
|
||||||
|
React.createElement(SeparateLine, { key: 0 }),
|
||||||
|
React.createElement(ScrollView, { key: 1, style: {
|
||||||
|
maxHeight: 200
|
||||||
|
} }, this.props.suggestionView)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
validateInput() {
|
||||||
|
if (this.props.validateInput) {
|
||||||
|
if (this.props.validateInput(this.props.value)) {
|
||||||
|
console.log('Input: valid');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log('Input: invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get isMultiLine() {
|
||||||
|
return this.props.numberOfLines && this.props.numberOfLines > 1;
|
||||||
|
}
|
||||||
|
get fontSize() {
|
||||||
|
return StyleManager.getFontSize('default');
|
||||||
|
}
|
||||||
|
get fontWeight() {
|
||||||
|
return StyleManager.getFontWeight('default');
|
||||||
|
}
|
||||||
|
get paddingVertical() {
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
get paddingHorizontal() {
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
export class CardContext {
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
export class FormContext {
|
export class FormContext {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.formFields = {};
|
this.formFields = {};
|
||||||
|
this.fieldListeners = {};
|
||||||
}
|
}
|
||||||
static getInstance() {
|
static getInstance() {
|
||||||
if (FormContext.sharedInstance === undefined) {
|
if (FormContext.sharedInstance === undefined) {
|
||||||
|
@ -14,6 +15,7 @@ export class FormContext {
|
||||||
value: value,
|
value: value,
|
||||||
validate: validate
|
validate: validate
|
||||||
};
|
};
|
||||||
|
this.getFieldListeners(id).forEach((listener) => listener(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getField(id) {
|
getField(id) {
|
||||||
|
@ -54,17 +56,6 @@ export class FormContext {
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
getCallbackParamData(params) {
|
|
||||||
if (params) {
|
|
||||||
return Object.keys(params).reduce((prev, current) => {
|
|
||||||
let formIndex = params[current];
|
|
||||||
console.log(formIndex);
|
|
||||||
prev[current] = this.getFieldValue(formIndex);
|
|
||||||
return prev;
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
validateField(id) {
|
validateField(id) {
|
||||||
let field = this.getField(id);
|
let field = this.getField(id);
|
||||||
if (field) {
|
if (field) {
|
||||||
|
@ -80,4 +71,19 @@ export class FormContext {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
registerFieldListener(id, listener) {
|
||||||
|
if (!this.fieldListeners[id]) {
|
||||||
|
this.fieldListeners[id] = [listener];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.fieldListeners[id].push(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getFieldListeners(id) {
|
||||||
|
let result = this.fieldListeners[id];
|
||||||
|
if (!result) {
|
||||||
|
result = [];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,9 @@ export class HostContext {
|
||||||
registerCallbackHandler(handler) {
|
registerCallbackHandler(handler) {
|
||||||
this.onCallback = handler;
|
this.onCallback = handler;
|
||||||
}
|
}
|
||||||
|
registerSelectActionHandler(handler) {
|
||||||
|
this.onSelectAction = handler;
|
||||||
|
}
|
||||||
applyConfig(configJson) {
|
applyConfig(configJson) {
|
||||||
this.config.combine(new HostConfig(configJson));
|
this.config.combine(new HostConfig(configJson));
|
||||||
}
|
}
|
||||||
|
@ -59,6 +62,9 @@ export class HostContext {
|
||||||
case ActionType.Submit:
|
case ActionType.Submit:
|
||||||
callback = this.onSubmit;
|
callback = this.onSubmit;
|
||||||
break;
|
break;
|
||||||
|
case ActionType.Select:
|
||||||
|
callback = this.onSelectAction;
|
||||||
|
break;
|
||||||
case 'focus':
|
case 'focus':
|
||||||
callback = this.onFocus;
|
callback = this.onFocus;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
import { Image } from 'react-native';
|
|
||||||
export class MediaContext {
|
|
||||||
constructor() {
|
|
||||||
this.mediaSizes = {};
|
|
||||||
}
|
|
||||||
static getInstance() {
|
|
||||||
if (MediaContext.sharedInstance === undefined) {
|
|
||||||
MediaContext.sharedInstance = new MediaContext();
|
|
||||||
}
|
|
||||||
return MediaContext.sharedInstance;
|
|
||||||
}
|
|
||||||
fetchImageSize(url, onSuccess, onFailure) {
|
|
||||||
let cache = this.getSize(url);
|
|
||||||
if (cache) {
|
|
||||||
onSuccess(cache.width, cache.height);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Image.getSize(url, (width, height) => {
|
|
||||||
this.cacheSize(url, { width: width, height: height });
|
|
||||||
onSuccess(width, height);
|
|
||||||
}, onFailure);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cacheSize(url, size) {
|
|
||||||
this.mediaSizes[url] = size;
|
|
||||||
}
|
|
||||||
getSize(url) {
|
|
||||||
return this.mediaSizes[url];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@ import { AbstractElement } from './AbstractElement';
|
||||||
export var ActionType;
|
export var ActionType;
|
||||||
(function (ActionType) {
|
(function (ActionType) {
|
||||||
ActionType["OpenUrl"] = "Action.OpenUrl";
|
ActionType["OpenUrl"] = "Action.OpenUrl";
|
||||||
|
ActionType["Select"] = "Action.Select";
|
||||||
ActionType["Submit"] = "Action.Submit";
|
ActionType["Submit"] = "Action.Submit";
|
||||||
ActionType["ShowCard"] = "Action.ShowCard";
|
ActionType["ShowCard"] = "Action.ShowCard";
|
||||||
ActionType["Callback"] = "Action.Callback";
|
ActionType["Callback"] = "Action.Callback";
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { ElementUtils } from '../../Utils/ElementUtils';
|
||||||
|
import { ActionElement } from '../Abstract/ActionElement';
|
||||||
|
export class SelectActionElement extends ActionElement {
|
||||||
|
constructor(json, parent) {
|
||||||
|
super(json, parent);
|
||||||
|
this.children = [];
|
||||||
|
if (this.isValid) {
|
||||||
|
this.title = json.selectedTextTitle;
|
||||||
|
this.subTitle = json.selectedTextSubTitle;
|
||||||
|
this.data = json.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get targetFormField() {
|
||||||
|
let targetInput = this.ancestorsAndSelf.find(element => ElementUtils.isSelectActionTarget(element.type));
|
||||||
|
if (targetInput) {
|
||||||
|
return targetInput.id;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
get scope() {
|
||||||
|
return this.ancestorsAndSelf.find(element => element.parent === undefined);
|
||||||
|
}
|
||||||
|
get requiredProperties() {
|
||||||
|
return ['type', 'selectedTextTitle', 'selectedTextSubTitle', 'data'];
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import { ActionType } from '../Abstract/ActionElement';
|
import { ActionType } from '../Abstract/ActionElement';
|
||||||
import { OpenUrlActionElement } from '../Actions/OpenUrlAction';
|
import { OpenUrlActionElement } from '../Actions/OpenUrlAction';
|
||||||
|
import { SelectActionElement } from '../Actions/SelectAction';
|
||||||
import { ShowCardActionElement } from '../Actions/ShowCardAction';
|
import { ShowCardActionElement } from '../Actions/ShowCardAction';
|
||||||
import { SubmitActionElement } from '../Actions/SubmitAction';
|
import { SubmitActionElement } from '../Actions/SubmitAction';
|
||||||
export class ActionFactory {
|
export class ActionFactory {
|
||||||
|
@ -18,6 +19,9 @@ export class ActionFactory {
|
||||||
case ActionType.ShowCard:
|
case ActionType.ShowCard:
|
||||||
action = new ShowCardActionElement(json, parent);
|
action = new ShowCardActionElement(json, parent);
|
||||||
break;
|
break;
|
||||||
|
case ActionType.Select:
|
||||||
|
action = new SelectActionElement(json, parent);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
action = undefined;
|
action = undefined;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -5,6 +5,10 @@ export class ElementUtils {
|
||||||
static isValue(type) {
|
static isValue(type) {
|
||||||
return ElementUtils.valueTypes.indexOf(type) >= 0;
|
return ElementUtils.valueTypes.indexOf(type) >= 0;
|
||||||
}
|
}
|
||||||
|
static isSelectActionTarget(type) {
|
||||||
|
return ElementUtils.selectActionTargetTypes.indexOf(type) >= 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ElementUtils.inputTypes = ['Input.Text', 'Input.Number', 'Input.Date', 'Input.Time', 'Input.Toggle', 'Input.ChoiceSet'];
|
ElementUtils.inputTypes = ['Input.Text', 'Input.Number', 'Input.Date', 'Input.Time', 'Input.Toggle', 'Input.ChoiceSet'];
|
||||||
ElementUtils.valueTypes = ['Fact', 'Input.Choice'];
|
ElementUtils.valueTypes = ['Fact', 'Input.Choice'];
|
||||||
|
ElementUtils.selectActionTargetTypes = ['Input.PeoplePicker'];
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { FactSetView } from '../Containers/FactSet';
|
||||||
import { ImageSetView } from '../Containers/ImageSet';
|
import { ImageSetView } from '../Containers/ImageSet';
|
||||||
import { DateInputView } from '../Inputs/DateInput';
|
import { DateInputView } from '../Inputs/DateInput';
|
||||||
import { NumberInputView } from '../Inputs/NumberInput';
|
import { NumberInputView } from '../Inputs/NumberInput';
|
||||||
|
import { PeoplePickerView } from '../Inputs/PeoplePicker';
|
||||||
import { TextInputView } from '../Inputs/TextInput';
|
import { TextInputView } from '../Inputs/TextInput';
|
||||||
import { TimeInputView } from '../Inputs/TimeInput';
|
import { TimeInputView } from '../Inputs/TimeInput';
|
||||||
export class ContentFactory {
|
export class ContentFactory {
|
||||||
|
@ -61,6 +62,8 @@ export class ContentFactory {
|
||||||
return (React.createElement(DateInputView, { key: 'DateInputView' + index, element: element, index: index, theme: theme }));
|
return (React.createElement(DateInputView, { key: 'DateInputView' + index, element: element, index: index, theme: theme }));
|
||||||
case ContentElementType.TimeInput:
|
case ContentElementType.TimeInput:
|
||||||
return (React.createElement(TimeInputView, { key: 'TimeInputView' + index, element: element, index: index, theme: theme }));
|
return (React.createElement(TimeInputView, { key: 'TimeInputView' + index, element: element, index: index, theme: theme }));
|
||||||
|
case ContentElementType.PeoplePicker:
|
||||||
|
return (React.createElement(PeoplePickerView, { key: 'PeoplePickerView' + index, element: element, index: index, theme: theme }));
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ export class DateInputView extends React.Component {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
get height() {
|
get height() {
|
||||||
return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
|
return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
|
||||||
}
|
}
|
||||||
get color() {
|
get color() {
|
||||||
if (this.state.focused) {
|
if (this.state.focused) {
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { LabelInput } from '../../Components/Inputs/LabelInput';
|
||||||
|
import { ActionContext } from '../../Contexts/ActionContext';
|
||||||
|
import { FormContext } from '../../Contexts/FormContext';
|
||||||
|
import { ActionType } from '../../Schema/Abstract/ActionElement';
|
||||||
|
import { CardElement } from '../../Schema/Cards/Card';
|
||||||
|
import { ContentFactory } from '../Factories/ContentFactory';
|
||||||
|
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
|
||||||
|
export class PeoplePickerView extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.onBlur = () => {
|
||||||
|
this.setState({ inputFocused: false });
|
||||||
|
};
|
||||||
|
this.onFocus = () => {
|
||||||
|
this.setState({ inputFocused: true });
|
||||||
|
};
|
||||||
|
this.onSuggestionCallback = (data) => {
|
||||||
|
this.setState({
|
||||||
|
suggestionCard: new CardElement(data, this.props.element),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.onRequestSuggestion = (input) => {
|
||||||
|
this.setState({
|
||||||
|
value: input,
|
||||||
|
}, () => {
|
||||||
|
const { element } = this.props;
|
||||||
|
if (element.callback) {
|
||||||
|
let callback = ActionContext.getGlobalInstance().getActionEventHandler(element.callback, this.onSuggestionCallback);
|
||||||
|
if (callback) {
|
||||||
|
callback({
|
||||||
|
actionType: ActionType.Callback,
|
||||||
|
func: this.populateApiParams,
|
||||||
|
name: 'populateApiParams'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.populateApiParams = (args) => {
|
||||||
|
if (args && args.formValidate) {
|
||||||
|
args.formData = this.extractParamData();
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
};
|
||||||
|
this.extractParamData = () => {
|
||||||
|
const { element } = this.props;
|
||||||
|
if (element.callback) {
|
||||||
|
const params = element.callback.parameters;
|
||||||
|
if (params) {
|
||||||
|
return Object.keys(params).reduce((prev, current) => {
|
||||||
|
let formIndex = params[current];
|
||||||
|
if (formIndex === element.id) {
|
||||||
|
prev[current] = this.state.value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
prev[current] = FormContext.getInstance().getFieldValue(formIndex);
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
this.storeListener = (value) => {
|
||||||
|
this.setState({
|
||||||
|
selected: JSON.parse(value),
|
||||||
|
suggestionCard: undefined,
|
||||||
|
inputFocused: true,
|
||||||
|
value: '',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const { element } = this.props;
|
||||||
|
if (element && element.isValid) {
|
||||||
|
this.state = {
|
||||||
|
value: '',
|
||||||
|
inputFocused: false,
|
||||||
|
selected: [],
|
||||||
|
suggestionCard: undefined,
|
||||||
|
};
|
||||||
|
FormContext.getInstance().updateField(element.id, JSON.stringify([]), true);
|
||||||
|
FormContext.getInstance().registerFieldListener(element.id, this.storeListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const { element, theme } = this.props;
|
||||||
|
if (!element || !element.isValid) {
|
||||||
|
return DebugOutputFactory.createDebugOutputBanner(element.type + '>>' + element.id + ' is not valid', theme, 'error');
|
||||||
|
}
|
||||||
|
return (React.createElement(LabelInput, { placeholder: element.placeholder, value: this.state.value, focused: this.state.inputFocused, labels: this.labels, suggestionView: ContentFactory.createElement(this.state.suggestionCard, 0, theme), onRequestSuggestion: this.onRequestSuggestion, onFocus: this.onFocus, onBlur: this.onBlur }));
|
||||||
|
}
|
||||||
|
get labels() {
|
||||||
|
return this.state.selected.map((contact) => {
|
||||||
|
return {
|
||||||
|
title: contact.Name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -75,7 +75,7 @@ export class TimeInputView extends React.Component {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
get height() {
|
get height() {
|
||||||
return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
|
return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
|
||||||
}
|
}
|
||||||
get color() {
|
get color() {
|
||||||
if (this.state.focused) {
|
if (this.state.focused) {
|
||||||
|
|
|
@ -50,6 +50,15 @@ export class CardRootView extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
this.onSelectAction = (args) => {
|
||||||
|
if (args) {
|
||||||
|
let currentValue = JSON.parse(FormContext.getInstance().getFieldValue(args.action.targetFormField));
|
||||||
|
if (currentValue) {
|
||||||
|
currentValue.push(args.formData);
|
||||||
|
FormContext.getInstance().updateField(args.action.targetFormField, JSON.stringify(currentValue), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
this.validateForm = (args) => {
|
this.validateForm = (args) => {
|
||||||
if (args) {
|
if (args) {
|
||||||
args.formValidate = args.action.scope.validateScope();
|
args.formValidate = args.action.scope.validateScope();
|
||||||
|
@ -68,9 +77,9 @@ export class CardRootView extends React.PureComponent {
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
};
|
};
|
||||||
this.populateCallbackParamData = (args) => {
|
this.populateSelectActionData = (args) => {
|
||||||
if (args && args.formValidate) {
|
if (args) {
|
||||||
args.formData = FormContext.getInstance().getCallbackParamData(args.action.parameters);
|
args.formData = Object.assign({}, (args.action.data || {}));
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
};
|
};
|
||||||
|
@ -80,6 +89,7 @@ export class CardRootView extends React.PureComponent {
|
||||||
hostContext.registerOpenUrlHandler(this.onOpenUrl);
|
hostContext.registerOpenUrlHandler(this.onOpenUrl);
|
||||||
hostContext.registerSubmitHandler(this.onSubmit);
|
hostContext.registerSubmitHandler(this.onSubmit);
|
||||||
hostContext.registerCallbackHandler(this.onCallback);
|
hostContext.registerCallbackHandler(this.onCallback);
|
||||||
|
hostContext.registerSelectActionHandler(this.onSelectAction);
|
||||||
hostContext.registerFocusHandler(this.props.onFocus);
|
hostContext.registerFocusHandler(this.props.onFocus);
|
||||||
hostContext.registerBlurHandler(this.props.onBlur);
|
hostContext.registerBlurHandler(this.props.onBlur);
|
||||||
hostContext.registerErrorHandler(this.props.onError);
|
hostContext.registerErrorHandler(this.props.onError);
|
||||||
|
@ -102,9 +112,9 @@ export class CardRootView extends React.PureComponent {
|
||||||
actionType: ActionType.Submit
|
actionType: ActionType.Submit
|
||||||
});
|
});
|
||||||
actionContext.registerHook({
|
actionContext.registerHook({
|
||||||
func: this.populateCallbackParamData,
|
func: this.populateSelectActionData,
|
||||||
name: 'populateCallbackParamData',
|
name: 'populateSelectActionData',
|
||||||
actionType: ActionType.Callback
|
actionType: ActionType.Select
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -13,28 +13,26 @@ import { Guid } from '../../Shared/Guid';
|
||||||
export class Touchable extends React.Component {
|
export class Touchable extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
this.testId = this.props.testId + Guid.newGuid();
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
DeviceEventEmitter.addListener('KeyEnter' + this.props.testId, this.props.onPress);
|
DeviceEventEmitter.addListener('KeyEnter' + this.testId, this.props.onPress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
DeviceEventEmitter.removeListener('KeyEnter' + this.props.testId, this.props.onPress);
|
DeviceEventEmitter.removeListener('KeyEnter' + this.testId, this.props.onPress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
const _a = this.props, { onPress, onLongPress, disabled, accessibilityLabel, accessibilityTraits, accessibilityComponentType, activeOpacity, hitSlop, style } = _a, otherProps = __rest(_a, ["onPress", "onLongPress", "disabled", "accessibilityLabel", "accessibilityTraits", "accessibilityComponentType", "activeOpacity", "hitSlop", "style"]);
|
const _a = this.props, { onPress, onLongPress, disabled, accessibilityLabel, accessibilityTraits, accessibilityComponentType, activeOpacity, hitSlop, style } = _a, otherProps = __rest(_a, ["onPress", "onLongPress", "disabled", "accessibilityLabel", "accessibilityTraits", "accessibilityComponentType", "activeOpacity", "hitSlop", "style"]);
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
return (React.createElement(TouchableNativeFeedback, { disabled: disabled, onPress: onPress, onLongPress: onLongPress, accessible: true, testID: this.uniqueTestId, useForeground: true, hitSlop: hitSlop, background: TouchableNativeFeedback.SelectableBackground(), accessibilityLabel: accessibilityLabel },
|
return (React.createElement(TouchableNativeFeedback, { disabled: disabled, onPress: onPress, onLongPress: onLongPress, accessible: true, testID: this.testId, useForeground: true, hitSlop: hitSlop, background: TouchableNativeFeedback.SelectableBackground(), accessibilityLabel: accessibilityLabel },
|
||||||
React.createElement(View, Object.assign({ style: style, onLayout: this.props.onLayout }, otherProps))));
|
React.createElement(View, Object.assign({ style: style, onLayout: this.props.onLayout }, otherProps))));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return (React.createElement(TouchableOpacity, { disabled: disabled, onPress: onPress, onLongPress: onLongPress, accessible: true, testID: this.uniqueTestId, activeOpacity: activeOpacity, style: style, hitSlop: hitSlop, accessibilityLabel: accessibilityLabel, onLayout: this.props.onLayout }, otherProps.children));
|
return (React.createElement(TouchableOpacity, { disabled: disabled, onPress: onPress, onLongPress: onLongPress, accessible: true, testID: this.testId, activeOpacity: activeOpacity, style: style, hitSlop: hitSlop, accessibilityLabel: accessibilityLabel, onLayout: this.props.onLayout }, otherProps.children));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get uniqueTestId() {
|
|
||||||
return this.props.testId + Guid.newGuid();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Text, View } from 'react-native';
|
||||||
|
import { StyleManager } from '../../Styles/StyleManager';
|
||||||
|
import { ImageBlock } from '../Basic/ImageBlock';
|
||||||
|
import { Touchable } from '../Basic/Touchable';
|
||||||
|
export class Contact extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
this.onPress = () => {
|
||||||
|
if (this.props.onSelect) {
|
||||||
|
this.props.onSelect(this.props.hiddenFields);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
if (this.props.onSelect) {
|
||||||
|
return this.renderTouchableBlock();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return this.renderNonTouchableBlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
renderTouchableBlock() {
|
||||||
|
return (React.createElement(Touchable, { onPress: this.onPress, style: {
|
||||||
|
alignSelf: 'stretch',
|
||||||
|
flexDirection: 'row'
|
||||||
|
} }, this.renderContent()));
|
||||||
|
}
|
||||||
|
renderNonTouchableBlock() {
|
||||||
|
return (React.createElement(View, { alignSelf: 'stretch', flexDirection: 'row' }, this.renderContent()));
|
||||||
|
}
|
||||||
|
renderContent() {
|
||||||
|
return [
|
||||||
|
React.createElement(ImageBlock, { url: this.props.avatar, mode: 'avatar', width: StyleManager.getImageSize('medium'), height: StyleManager.getImageSize('medium') }),
|
||||||
|
React.createElement(View, null,
|
||||||
|
React.createElement(Text, { accessible: true, style: {
|
||||||
|
color: StyleManager.getColor('default', this.props.theme, false),
|
||||||
|
fontSize: StyleManager.getFontSize('default'),
|
||||||
|
fontWeight: StyleManager.getFontWeight('default'),
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
textAlign: StyleManager.getTextAlign('left'),
|
||||||
|
flexWrap: StyleManager.getWrap(true),
|
||||||
|
} }, this.props.mainInfo),
|
||||||
|
React.createElement(Text, { accessible: true, style: {
|
||||||
|
color: StyleManager.getColor('default', this.props.theme, true),
|
||||||
|
fontSize: StyleManager.getFontSize('small'),
|
||||||
|
fontWeight: StyleManager.getFontWeight('default'),
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
textAlign: StyleManager.getTextAlign('left'),
|
||||||
|
flexWrap: StyleManager.getWrap(true),
|
||||||
|
} }, this.props.subInfo))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -87,7 +87,7 @@ export class InputBox extends React.Component {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
get height() {
|
get height() {
|
||||||
return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
|
return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
|
||||||
}
|
}
|
||||||
get color() {
|
get color() {
|
||||||
if (this.state.focused) {
|
if (this.state.focused) {
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { ScrollView, Text, TextInput, View } from 'react-native';
|
||||||
|
import { StyleManager } from '../../Styles/StyleManager';
|
||||||
|
import { SeparateLine } from '../Basic/SeparateLine';
|
||||||
|
export class LabelInput extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.onValueChange = (value) => {
|
||||||
|
if (this.props.onRequestSuggestion) {
|
||||||
|
this.props.onRequestSuggestion(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.onBlur = () => {
|
||||||
|
this.setState({ focused: false }, () => {
|
||||||
|
this.validateInput();
|
||||||
|
if (this.props.onBlur) {
|
||||||
|
this.props.onBlur();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.onFocus = () => {
|
||||||
|
this.setState({
|
||||||
|
focused: true
|
||||||
|
}, () => {
|
||||||
|
if (this.props.onFocus) {
|
||||||
|
this.props.onFocus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.state = {
|
||||||
|
focused: this.props.focused,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (!prevState.focused && this.props.focused) {
|
||||||
|
this.setState({
|
||||||
|
focused: true,
|
||||||
|
}, () => {
|
||||||
|
if (this.inputBox) {
|
||||||
|
this.inputBox.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return (React.createElement(View, { style: {
|
||||||
|
flex: this.props.flex,
|
||||||
|
} },
|
||||||
|
this.renderInputArea(),
|
||||||
|
this.renderSuggestions()));
|
||||||
|
}
|
||||||
|
renderInputArea() {
|
||||||
|
return (React.createElement(View, { style: {
|
||||||
|
alignSelf: 'stretch',
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
backgroundColor: this.backgroundColor,
|
||||||
|
borderColor: this.borderColor,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: 4,
|
||||||
|
width: this.props.width,
|
||||||
|
marginTop: this.props.marginTop,
|
||||||
|
marginRight: this.props.marginRight,
|
||||||
|
marginBottom: this.props.marginBottom,
|
||||||
|
marginLeft: this.props.marginLeft,
|
||||||
|
} },
|
||||||
|
this.renderLabels(),
|
||||||
|
this.renderInputBox()));
|
||||||
|
}
|
||||||
|
renderLabels() {
|
||||||
|
if (this.props.labels) {
|
||||||
|
return this.props.labels.map((label, index) => {
|
||||||
|
return (React.createElement(Text, { key: 'Label' + index, style: {
|
||||||
|
fontSize: this.fontSize,
|
||||||
|
fontWeight: this.fontWeight,
|
||||||
|
color: this.backgroundColor,
|
||||||
|
backgroundColor: this.color,
|
||||||
|
paddingTop: this.paddingVertical - 6,
|
||||||
|
paddingBottom: this.paddingVertical - 6,
|
||||||
|
borderRadius: 4,
|
||||||
|
paddingLeft: 6,
|
||||||
|
paddingRight: 6,
|
||||||
|
marginTop: 6,
|
||||||
|
marginBottom: 6,
|
||||||
|
marginLeft: 6,
|
||||||
|
} }, label.title));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
renderInputBox() {
|
||||||
|
return (React.createElement(TextInput, { ref: ref => this.inputBox = ref, style: [
|
||||||
|
{
|
||||||
|
flex: 1,
|
||||||
|
color: this.color,
|
||||||
|
fontSize: this.fontSize,
|
||||||
|
fontWeight: this.fontWeight,
|
||||||
|
paddingTop: this.paddingVertical,
|
||||||
|
paddingRight: this.paddingHorizontal,
|
||||||
|
paddingBottom: this.paddingVertical,
|
||||||
|
paddingLeft: this.paddingHorizontal,
|
||||||
|
},
|
||||||
|
this.props.style
|
||||||
|
], multiline: this.isMultiLine, numberOfLines: this.props.numberOfLines, keyboardType: this.props.keyboardType, blurOnSubmit: !this.isMultiLine, placeholder: this.props.placeholder, value: this.props.value, returnKeyType: this.props.returnKeyType, underlineColorAndroid: 'transparent', importantForAccessibility: 'no-hide-descendants', onChangeText: this.onValueChange, onFocus: this.onFocus, onBlur: this.onBlur }));
|
||||||
|
}
|
||||||
|
renderSuggestions() {
|
||||||
|
if (this.props.suggestionView) {
|
||||||
|
return [
|
||||||
|
React.createElement(SeparateLine, { key: 0 }),
|
||||||
|
React.createElement(ScrollView, { key: 1, style: {
|
||||||
|
maxHeight: 200
|
||||||
|
} }, this.props.suggestionView)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
validateInput() {
|
||||||
|
if (this.props.validateInput) {
|
||||||
|
if (this.props.validateInput(this.props.value)) {
|
||||||
|
console.log('Input: valid');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log('Input: invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get isMultiLine() {
|
||||||
|
return this.props.numberOfLines && this.props.numberOfLines > 1;
|
||||||
|
}
|
||||||
|
get fontSize() {
|
||||||
|
return StyleManager.getFontSize('default');
|
||||||
|
}
|
||||||
|
get fontWeight() {
|
||||||
|
return StyleManager.getFontWeight('default');
|
||||||
|
}
|
||||||
|
get paddingVertical() {
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
get paddingHorizontal() {
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
export class CardContext {
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
export class FormContext {
|
export class FormContext {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.formFields = {};
|
this.formFields = {};
|
||||||
|
this.fieldListeners = {};
|
||||||
}
|
}
|
||||||
static getInstance() {
|
static getInstance() {
|
||||||
if (FormContext.sharedInstance === undefined) {
|
if (FormContext.sharedInstance === undefined) {
|
||||||
|
@ -14,6 +15,7 @@ export class FormContext {
|
||||||
value: value,
|
value: value,
|
||||||
validate: validate
|
validate: validate
|
||||||
};
|
};
|
||||||
|
this.getFieldListeners(id).forEach((listener) => listener(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getField(id) {
|
getField(id) {
|
||||||
|
@ -54,17 +56,6 @@ export class FormContext {
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
getCallbackParamData(params) {
|
|
||||||
if (params) {
|
|
||||||
return Object.keys(params).reduce((prev, current) => {
|
|
||||||
let formIndex = params[current];
|
|
||||||
console.log(formIndex);
|
|
||||||
prev[current] = this.getFieldValue(formIndex);
|
|
||||||
return prev;
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
validateField(id) {
|
validateField(id) {
|
||||||
let field = this.getField(id);
|
let field = this.getField(id);
|
||||||
if (field) {
|
if (field) {
|
||||||
|
@ -80,4 +71,19 @@ export class FormContext {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
registerFieldListener(id, listener) {
|
||||||
|
if (!this.fieldListeners[id]) {
|
||||||
|
this.fieldListeners[id] = [listener];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.fieldListeners[id].push(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getFieldListeners(id) {
|
||||||
|
let result = this.fieldListeners[id];
|
||||||
|
if (!result) {
|
||||||
|
result = [];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,9 @@ export class HostContext {
|
||||||
registerCallbackHandler(handler) {
|
registerCallbackHandler(handler) {
|
||||||
this.onCallback = handler;
|
this.onCallback = handler;
|
||||||
}
|
}
|
||||||
|
registerSelectActionHandler(handler) {
|
||||||
|
this.onSelectAction = handler;
|
||||||
|
}
|
||||||
applyConfig(configJson) {
|
applyConfig(configJson) {
|
||||||
this.config.combine(new HostConfig(configJson));
|
this.config.combine(new HostConfig(configJson));
|
||||||
}
|
}
|
||||||
|
@ -59,6 +62,9 @@ export class HostContext {
|
||||||
case ActionType.Submit:
|
case ActionType.Submit:
|
||||||
callback = this.onSubmit;
|
callback = this.onSubmit;
|
||||||
break;
|
break;
|
||||||
|
case ActionType.Select:
|
||||||
|
callback = this.onSelectAction;
|
||||||
|
break;
|
||||||
case 'focus':
|
case 'focus':
|
||||||
callback = this.onFocus;
|
callback = this.onFocus;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
import { Image } from 'react-native';
|
|
||||||
export class MediaContext {
|
|
||||||
constructor() {
|
|
||||||
this.mediaSizes = {};
|
|
||||||
}
|
|
||||||
static getInstance() {
|
|
||||||
if (MediaContext.sharedInstance === undefined) {
|
|
||||||
MediaContext.sharedInstance = new MediaContext();
|
|
||||||
}
|
|
||||||
return MediaContext.sharedInstance;
|
|
||||||
}
|
|
||||||
fetchImageSize(url, onSuccess, onFailure) {
|
|
||||||
let cache = this.getSize(url);
|
|
||||||
if (cache) {
|
|
||||||
onSuccess(cache.width, cache.height);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Image.getSize(url, (width, height) => {
|
|
||||||
this.cacheSize(url, { width: width, height: height });
|
|
||||||
onSuccess(width, height);
|
|
||||||
}, onFailure);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cacheSize(url, size) {
|
|
||||||
this.mediaSizes[url] = size;
|
|
||||||
}
|
|
||||||
getSize(url) {
|
|
||||||
return this.mediaSizes[url];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@ import { AbstractElement } from './AbstractElement';
|
||||||
export var ActionType;
|
export var ActionType;
|
||||||
(function (ActionType) {
|
(function (ActionType) {
|
||||||
ActionType["OpenUrl"] = "Action.OpenUrl";
|
ActionType["OpenUrl"] = "Action.OpenUrl";
|
||||||
|
ActionType["Select"] = "Action.Select";
|
||||||
ActionType["Submit"] = "Action.Submit";
|
ActionType["Submit"] = "Action.Submit";
|
||||||
ActionType["ShowCard"] = "Action.ShowCard";
|
ActionType["ShowCard"] = "Action.ShowCard";
|
||||||
ActionType["Callback"] = "Action.Callback";
|
ActionType["Callback"] = "Action.Callback";
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { ElementUtils } from '../../Utils/ElementUtils';
|
||||||
|
import { ActionElement } from '../Abstract/ActionElement';
|
||||||
|
export class SelectActionElement extends ActionElement {
|
||||||
|
constructor(json, parent) {
|
||||||
|
super(json, parent);
|
||||||
|
this.children = [];
|
||||||
|
if (this.isValid) {
|
||||||
|
this.title = json.selectedTextTitle;
|
||||||
|
this.subTitle = json.selectedTextSubTitle;
|
||||||
|
this.data = json.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get targetFormField() {
|
||||||
|
let targetInput = this.ancestorsAndSelf.find(element => ElementUtils.isSelectActionTarget(element.type));
|
||||||
|
if (targetInput) {
|
||||||
|
return targetInput.id;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
get scope() {
|
||||||
|
return this.ancestorsAndSelf.find(element => element.parent === undefined);
|
||||||
|
}
|
||||||
|
get requiredProperties() {
|
||||||
|
return ['type', 'selectedTextTitle', 'selectedTextSubTitle', 'data'];
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import { ActionType } from '../Abstract/ActionElement';
|
import { ActionType } from '../Abstract/ActionElement';
|
||||||
import { OpenUrlActionElement } from '../Actions/OpenUrlAction';
|
import { OpenUrlActionElement } from '../Actions/OpenUrlAction';
|
||||||
|
import { SelectActionElement } from '../Actions/SelectAction';
|
||||||
import { ShowCardActionElement } from '../Actions/ShowCardAction';
|
import { ShowCardActionElement } from '../Actions/ShowCardAction';
|
||||||
import { SubmitActionElement } from '../Actions/SubmitAction';
|
import { SubmitActionElement } from '../Actions/SubmitAction';
|
||||||
export class ActionFactory {
|
export class ActionFactory {
|
||||||
|
@ -18,6 +19,9 @@ export class ActionFactory {
|
||||||
case ActionType.ShowCard:
|
case ActionType.ShowCard:
|
||||||
action = new ShowCardActionElement(json, parent);
|
action = new ShowCardActionElement(json, parent);
|
||||||
break;
|
break;
|
||||||
|
case ActionType.Select:
|
||||||
|
action = new SelectActionElement(json, parent);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
action = undefined;
|
action = undefined;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -5,6 +5,10 @@ export class ElementUtils {
|
||||||
static isValue(type) {
|
static isValue(type) {
|
||||||
return ElementUtils.valueTypes.indexOf(type) >= 0;
|
return ElementUtils.valueTypes.indexOf(type) >= 0;
|
||||||
}
|
}
|
||||||
|
static isSelectActionTarget(type) {
|
||||||
|
return ElementUtils.selectActionTargetTypes.indexOf(type) >= 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ElementUtils.inputTypes = ['Input.Text', 'Input.Number', 'Input.Date', 'Input.Time', 'Input.Toggle', 'Input.ChoiceSet'];
|
ElementUtils.inputTypes = ['Input.Text', 'Input.Number', 'Input.Date', 'Input.Time', 'Input.Toggle', 'Input.ChoiceSet'];
|
||||||
ElementUtils.valueTypes = ['Fact', 'Input.Choice'];
|
ElementUtils.valueTypes = ['Fact', 'Input.Choice'];
|
||||||
|
ElementUtils.selectActionTargetTypes = ['Input.PeoplePicker'];
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { FactSetView } from '../Containers/FactSet';
|
||||||
import { ImageSetView } from '../Containers/ImageSet';
|
import { ImageSetView } from '../Containers/ImageSet';
|
||||||
import { DateInputView } from '../Inputs/DateInput';
|
import { DateInputView } from '../Inputs/DateInput';
|
||||||
import { NumberInputView } from '../Inputs/NumberInput';
|
import { NumberInputView } from '../Inputs/NumberInput';
|
||||||
|
import { PeoplePickerView } from '../Inputs/PeoplePicker';
|
||||||
import { TextInputView } from '../Inputs/TextInput';
|
import { TextInputView } from '../Inputs/TextInput';
|
||||||
import { TimeInputView } from '../Inputs/TimeInput';
|
import { TimeInputView } from '../Inputs/TimeInput';
|
||||||
export class ContentFactory {
|
export class ContentFactory {
|
||||||
|
@ -61,6 +62,8 @@ export class ContentFactory {
|
||||||
return (React.createElement(DateInputView, { key: 'DateInputView' + index, element: element, index: index, theme: theme }));
|
return (React.createElement(DateInputView, { key: 'DateInputView' + index, element: element, index: index, theme: theme }));
|
||||||
case ContentElementType.TimeInput:
|
case ContentElementType.TimeInput:
|
||||||
return (React.createElement(TimeInputView, { key: 'TimeInputView' + index, element: element, index: index, theme: theme }));
|
return (React.createElement(TimeInputView, { key: 'TimeInputView' + index, element: element, index: index, theme: theme }));
|
||||||
|
case ContentElementType.PeoplePicker:
|
||||||
|
return (React.createElement(PeoplePickerView, { key: 'PeoplePickerView' + index, element: element, index: index, theme: theme }));
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ export class DateInputView extends React.Component {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
get height() {
|
get height() {
|
||||||
return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
|
return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
|
||||||
}
|
}
|
||||||
get color() {
|
get color() {
|
||||||
if (this.state.focused) {
|
if (this.state.focused) {
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { LabelInput } from '../../Components/Inputs/LabelInput';
|
||||||
|
import { ActionContext } from '../../Contexts/ActionContext';
|
||||||
|
import { FormContext } from '../../Contexts/FormContext';
|
||||||
|
import { ActionType } from '../../Schema/Abstract/ActionElement';
|
||||||
|
import { CardElement } from '../../Schema/Cards/Card';
|
||||||
|
import { ContentFactory } from '../Factories/ContentFactory';
|
||||||
|
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
|
||||||
|
export class PeoplePickerView extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.onBlur = () => {
|
||||||
|
this.setState({ inputFocused: false });
|
||||||
|
};
|
||||||
|
this.onFocus = () => {
|
||||||
|
this.setState({ inputFocused: true });
|
||||||
|
};
|
||||||
|
this.onSuggestionCallback = (data) => {
|
||||||
|
this.setState({
|
||||||
|
suggestionCard: new CardElement(data, this.props.element),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.onRequestSuggestion = (input) => {
|
||||||
|
this.setState({
|
||||||
|
value: input,
|
||||||
|
}, () => {
|
||||||
|
const { element } = this.props;
|
||||||
|
if (element.callback) {
|
||||||
|
let callback = ActionContext.getGlobalInstance().getActionEventHandler(element.callback, this.onSuggestionCallback);
|
||||||
|
if (callback) {
|
||||||
|
callback({
|
||||||
|
actionType: ActionType.Callback,
|
||||||
|
func: this.populateApiParams,
|
||||||
|
name: 'populateApiParams'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.populateApiParams = (args) => {
|
||||||
|
if (args && args.formValidate) {
|
||||||
|
args.formData = this.extractParamData();
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
};
|
||||||
|
this.extractParamData = () => {
|
||||||
|
const { element } = this.props;
|
||||||
|
if (element.callback) {
|
||||||
|
const params = element.callback.parameters;
|
||||||
|
if (params) {
|
||||||
|
return Object.keys(params).reduce((prev, current) => {
|
||||||
|
let formIndex = params[current];
|
||||||
|
if (formIndex === element.id) {
|
||||||
|
prev[current] = this.state.value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
prev[current] = FormContext.getInstance().getFieldValue(formIndex);
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
this.storeListener = (value) => {
|
||||||
|
this.setState({
|
||||||
|
selected: JSON.parse(value),
|
||||||
|
suggestionCard: undefined,
|
||||||
|
inputFocused: true,
|
||||||
|
value: '',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const { element } = this.props;
|
||||||
|
if (element && element.isValid) {
|
||||||
|
this.state = {
|
||||||
|
value: '',
|
||||||
|
inputFocused: false,
|
||||||
|
selected: [],
|
||||||
|
suggestionCard: undefined,
|
||||||
|
};
|
||||||
|
FormContext.getInstance().updateField(element.id, JSON.stringify([]), true);
|
||||||
|
FormContext.getInstance().registerFieldListener(element.id, this.storeListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const { element, theme } = this.props;
|
||||||
|
if (!element || !element.isValid) {
|
||||||
|
return DebugOutputFactory.createDebugOutputBanner(element.type + '>>' + element.id + ' is not valid', theme, 'error');
|
||||||
|
}
|
||||||
|
return (React.createElement(LabelInput, { placeholder: element.placeholder, value: this.state.value, focused: this.state.inputFocused, labels: this.labels, suggestionView: ContentFactory.createElement(this.state.suggestionCard, 0, theme), onRequestSuggestion: this.onRequestSuggestion, onFocus: this.onFocus, onBlur: this.onBlur }));
|
||||||
|
}
|
||||||
|
get labels() {
|
||||||
|
return this.state.selected.map((contact) => {
|
||||||
|
return {
|
||||||
|
title: contact.Name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -75,7 +75,7 @@ export class TimeInputView extends React.Component {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
get height() {
|
get height() {
|
||||||
return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
|
return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
|
||||||
}
|
}
|
||||||
get color() {
|
get color() {
|
||||||
if (this.state.focused) {
|
if (this.state.focused) {
|
||||||
|
|
|
@ -50,6 +50,15 @@ export class CardRootView extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
this.onSelectAction = (args) => {
|
||||||
|
if (args) {
|
||||||
|
let currentValue = JSON.parse(FormContext.getInstance().getFieldValue(args.action.targetFormField));
|
||||||
|
if (currentValue) {
|
||||||
|
currentValue.push(args.formData);
|
||||||
|
FormContext.getInstance().updateField(args.action.targetFormField, JSON.stringify(currentValue), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
this.validateForm = (args) => {
|
this.validateForm = (args) => {
|
||||||
if (args) {
|
if (args) {
|
||||||
args.formValidate = args.action.scope.validateScope();
|
args.formValidate = args.action.scope.validateScope();
|
||||||
|
@ -68,9 +77,9 @@ export class CardRootView extends React.PureComponent {
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
};
|
};
|
||||||
this.populateCallbackParamData = (args) => {
|
this.populateSelectActionData = (args) => {
|
||||||
if (args && args.formValidate) {
|
if (args) {
|
||||||
args.formData = FormContext.getInstance().getCallbackParamData(args.action.parameters);
|
args.formData = Object.assign({}, (args.action.data || {}));
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
};
|
};
|
||||||
|
@ -80,6 +89,7 @@ export class CardRootView extends React.PureComponent {
|
||||||
hostContext.registerOpenUrlHandler(this.onOpenUrl);
|
hostContext.registerOpenUrlHandler(this.onOpenUrl);
|
||||||
hostContext.registerSubmitHandler(this.onSubmit);
|
hostContext.registerSubmitHandler(this.onSubmit);
|
||||||
hostContext.registerCallbackHandler(this.onCallback);
|
hostContext.registerCallbackHandler(this.onCallback);
|
||||||
|
hostContext.registerSelectActionHandler(this.onSelectAction);
|
||||||
hostContext.registerFocusHandler(this.props.onFocus);
|
hostContext.registerFocusHandler(this.props.onFocus);
|
||||||
hostContext.registerBlurHandler(this.props.onBlur);
|
hostContext.registerBlurHandler(this.props.onBlur);
|
||||||
hostContext.registerErrorHandler(this.props.onError);
|
hostContext.registerErrorHandler(this.props.onError);
|
||||||
|
@ -102,9 +112,9 @@ export class CardRootView extends React.PureComponent {
|
||||||
actionType: ActionType.Submit
|
actionType: ActionType.Submit
|
||||||
});
|
});
|
||||||
actionContext.registerHook({
|
actionContext.registerHook({
|
||||||
func: this.populateCallbackParamData,
|
func: this.populateSelectActionData,
|
||||||
name: 'populateCallbackParamData',
|
name: 'populateSelectActionData',
|
||||||
actionType: ActionType.Callback
|
actionType: ActionType.Select
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -31,67 +31,7 @@ export default class App extends React.Component {
|
||||||
padding: 10,
|
padding: 10,
|
||||||
backgroundColor: 'white',
|
backgroundColor: 'white',
|
||||||
}}>
|
}}>
|
||||||
<AdaptiveCards adaptiveCard={mockData.bingMap} />
|
<AdaptiveCards adaptiveCard={mockData.peoplePicker} onCallback={this.onCallback}/>
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.dinning} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.vocabulary} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.fact} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.showVideo} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.weatherCompact} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={BingAnswer.heightOfEiffelTower} overrideStyle={cardOverrideStyle} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={BingAnswer.highestMountionInTheWorld} overrideStyle={cardOverrideStyle} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={BingAnswer.latestNews} overrideStyle={cardOverrideStyle} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={BingAnswer.microsoftStock} overrideStyle={cardOverrideStyle} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={BingAnswer.showMeFunnyVideo} overrideStyle={cardOverrideStyle} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={BingAnswer.timeInLondon} overrideStyle={cardOverrideStyle} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={BingAnswer.whatIsTheWeather} overrideStyle={cardOverrideStyle} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.adaptiveUpdate} overrideStyle={cardOverrideStyle} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.peoplePicker} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.emailSent} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.searchEmail} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.readEmail} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.news} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.adaptiveUpdate} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.flightItinerary} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.flightUpdate} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.foodOrder} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.imageGallery} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.inputForm} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.inputs} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.restaurant} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.solitaire} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.sportsGameUpdate} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.stockUpdate} />
|
|
||||||
{this.renderGap()}
|
|
||||||
<AdaptiveCards adaptiveCard={mockData.weatherLarge} />
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -99,4 +39,8 @@ export default class App extends React.Component {
|
||||||
renderGap() {
|
renderGap() {
|
||||||
return <View style={{ height: cardGap }} />;
|
return <View style={{ height: cardGap }} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onCallback = (url, data) => {
|
||||||
|
return Promise.resolve(mockData.peopleSuggestion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,8 @@ var searchEmail = require('./skills/search_email.json');
|
||||||
var emailSent = require('./skills/email_sent.json');
|
var emailSent = require('./skills/email_sent.json');
|
||||||
var sendText = require('./skills/send_text.json');
|
var sendText = require('./skills/send_text.json');
|
||||||
var sendTextContact = require('./skills/send_text_contact_disam.json');
|
var sendTextContact = require('./skills/send_text_contact_disam.json');
|
||||||
var peoplePicker = require('./peoplepicker.json');
|
var peoplePicker = require('./peoplePicker.json');
|
||||||
|
var peopleSuggestion = require('./peopleSuggestion.json');
|
||||||
var showVideo = require('./showVideo.json');
|
var showVideo = require('./showVideo.json');
|
||||||
var fact = require('./fact.json');
|
var fact = require('./fact.json');
|
||||||
var vocabulary = require('./vocabulary.json');
|
var vocabulary = require('./vocabulary.json');
|
||||||
|
@ -52,4 +53,5 @@ exports["default"] = {
|
||||||
vocabulary: vocabulary,
|
vocabulary: vocabulary,
|
||||||
dinning: dinning,
|
dinning: dinning,
|
||||||
bingMap: bingMap,
|
bingMap: bingMap,
|
||||||
|
peopleSuggestion: peopleSuggestion,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,397 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||||
|
"type": "AdaptiveCard",
|
||||||
|
"version": "1.0",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "Container",
|
||||||
|
"id": "peoplePickerContent",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "ColumnSet",
|
||||||
|
"separator": true,
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Auto",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "Image",
|
||||||
|
"size": "medium",
|
||||||
|
"style": "person",
|
||||||
|
"url": "/cortana/autosuggest/people/photo?provider=Office365&uid=Richard.Zhao%40microsoft.com",
|
||||||
|
"altText": "RZ"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Stretch",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"weight": "bolder",
|
||||||
|
"text": "Richard Zhao",
|
||||||
|
"wrap": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"isSubtle": true,
|
||||||
|
"text": "Richard.Zhao@microsoft.com",
|
||||||
|
"wrap": true,
|
||||||
|
"spacing": "none",
|
||||||
|
"separation": "none"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selectAction": {
|
||||||
|
"type": "Action.Select",
|
||||||
|
"selectedTextTitle": "Richard Zhao",
|
||||||
|
"selectedTextSubTitle": "@microsoft.com",
|
||||||
|
"data": {
|
||||||
|
"Name": "Richard Zhao",
|
||||||
|
"Address": "Richard.Zhao@microsoft.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ColumnSet",
|
||||||
|
"separator": true,
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Auto",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "Image",
|
||||||
|
"size": "medium",
|
||||||
|
"style": "person",
|
||||||
|
"url": "/cortana/autosuggest/people/photo?provider=Office365&uid=quanliu%40microsoft.com",
|
||||||
|
"altText": "RL"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Stretch",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"weight": "bolder",
|
||||||
|
"text": "Richard Liu",
|
||||||
|
"wrap": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"isSubtle": true,
|
||||||
|
"text": "quanliu@microsoft.com",
|
||||||
|
"wrap": true,
|
||||||
|
"spacing": "none",
|
||||||
|
"separation": "none"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selectAction": {
|
||||||
|
"type": "Action.Select",
|
||||||
|
"selectedTextTitle": "Richard Liu",
|
||||||
|
"selectedTextSubTitle": "@microsoft.com",
|
||||||
|
"data": {
|
||||||
|
"Name": "Richard Liu",
|
||||||
|
"Address": "quanliu@microsoft.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ColumnSet",
|
||||||
|
"separator": true,
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Auto",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "Image",
|
||||||
|
"size": "medium",
|
||||||
|
"style": "person",
|
||||||
|
"url": "/cortana/autosuggest/people/photo?provider=Office365&uid=richzdir%40microsoft.com",
|
||||||
|
"altText": "RD"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Stretch",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"weight": "bolder",
|
||||||
|
"text": "RichZ's directs",
|
||||||
|
"wrap": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"isSubtle": true,
|
||||||
|
"text": "richzdir@microsoft.com",
|
||||||
|
"wrap": true,
|
||||||
|
"spacing": "none",
|
||||||
|
"separation": "none"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selectAction": {
|
||||||
|
"type": "Action.Select",
|
||||||
|
"selectedTextTitle": "RichZ's directs",
|
||||||
|
"selectedTextSubTitle": "@microsoft.com",
|
||||||
|
"data": {
|
||||||
|
"Name": "RichZ's directs",
|
||||||
|
"Address": "richzdir@microsoft.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ColumnSet",
|
||||||
|
"separator": true,
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Auto",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "Image",
|
||||||
|
"size": "medium",
|
||||||
|
"style": "person",
|
||||||
|
"url": "/cortana/autosuggest/people/photo?provider=Office365&uid=jordir%40microsoft.com",
|
||||||
|
"altText": "JR"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Stretch",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"weight": "bolder",
|
||||||
|
"text": "Jordi Ribas",
|
||||||
|
"wrap": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"isSubtle": true,
|
||||||
|
"text": "jordir@microsoft.com",
|
||||||
|
"wrap": true,
|
||||||
|
"spacing": "none",
|
||||||
|
"separation": "none"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selectAction": {
|
||||||
|
"type": "Action.Select",
|
||||||
|
"selectedTextTitle": "Jordi Ribas",
|
||||||
|
"selectedTextSubTitle": "@microsoft.com",
|
||||||
|
"data": {
|
||||||
|
"Name": "Jordi Ribas",
|
||||||
|
"Address": "jordir@microsoft.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ColumnSet",
|
||||||
|
"separator": true,
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Auto",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "Image",
|
||||||
|
"size": "medium",
|
||||||
|
"style": "person",
|
||||||
|
"url": "/cortana/autosuggest/people/photo?provider=Office365&uid=richq%40microsoft.com",
|
||||||
|
"altText": "RQ"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Stretch",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"weight": "bolder",
|
||||||
|
"text": "Richard Qian",
|
||||||
|
"wrap": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"isSubtle": true,
|
||||||
|
"text": "richq@microsoft.com",
|
||||||
|
"wrap": true,
|
||||||
|
"spacing": "none",
|
||||||
|
"separation": "none"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selectAction": {
|
||||||
|
"type": "Action.Select",
|
||||||
|
"selectedTextTitle": "Richard Qian",
|
||||||
|
"selectedTextSubTitle": "@microsoft.com",
|
||||||
|
"data": {
|
||||||
|
"Name": "Richard Qian",
|
||||||
|
"Address": "richq@microsoft.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ColumnSet",
|
||||||
|
"separator": true,
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Auto",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "Image",
|
||||||
|
"size": "medium",
|
||||||
|
"style": "person",
|
||||||
|
"url": "/cortana/autosuggest/people/photo?provider=Office365&uid=richard.zhao%40outlook.com",
|
||||||
|
"altText": "RZ"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Stretch",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"weight": "bolder",
|
||||||
|
"text": "Richard Zhao",
|
||||||
|
"wrap": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"isSubtle": true,
|
||||||
|
"text": "richard.zhao@outlook.com",
|
||||||
|
"wrap": true,
|
||||||
|
"spacing": "none",
|
||||||
|
"separation": "none"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selectAction": {
|
||||||
|
"type": "Action.Select",
|
||||||
|
"selectedTextTitle": "Richard Zhao",
|
||||||
|
"selectedTextSubTitle": "@outlook.com",
|
||||||
|
"data": {
|
||||||
|
"Name": "Richard Zhao",
|
||||||
|
"Address": "richard.zhao@outlook.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ColumnSet",
|
||||||
|
"separator": true,
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Auto",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "Image",
|
||||||
|
"size": "medium",
|
||||||
|
"style": "person",
|
||||||
|
"url": "/cortana/autosuggest/people/photo?provider=Office365&uid=Eric.Carter%40microsoft.com",
|
||||||
|
"altText": "EC"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Stretch",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"weight": "bolder",
|
||||||
|
"text": "Eric Carter",
|
||||||
|
"wrap": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"isSubtle": true,
|
||||||
|
"text": "Eric.Carter@microsoft.com",
|
||||||
|
"wrap": true,
|
||||||
|
"spacing": "none",
|
||||||
|
"separation": "none"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selectAction": {
|
||||||
|
"type": "Action.Select",
|
||||||
|
"selectedTextTitle": "Eric Carter",
|
||||||
|
"selectedTextSubTitle": "@microsoft.com",
|
||||||
|
"data": {
|
||||||
|
"Name": "Eric Carter",
|
||||||
|
"Address": "Eric.Carter@microsoft.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ColumnSet",
|
||||||
|
"separator": true,
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Auto",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "Image",
|
||||||
|
"size": "medium",
|
||||||
|
"style": "person",
|
||||||
|
"url": "/cortana/autosuggest/people/photo?provider=Office365&uid=eryang%40microsoft.com",
|
||||||
|
"altText": "EY"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Column",
|
||||||
|
"width": "Stretch",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"weight": "bolder",
|
||||||
|
"text": "Eric Yang",
|
||||||
|
"wrap": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"isSubtle": true,
|
||||||
|
"text": "eryang@microsoft.com",
|
||||||
|
"wrap": true,
|
||||||
|
"spacing": "none",
|
||||||
|
"separation": "none"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selectAction": {
|
||||||
|
"type": "Action.Select",
|
||||||
|
"selectedTextTitle": "Eric Yang",
|
||||||
|
"selectedTextSubTitle": "@microsoft.com",
|
||||||
|
"data": {
|
||||||
|
"Name": "Eric Yang",
|
||||||
|
"Address": "eryang@microsoft.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
24
package.json
24
package.json
|
@ -23,20 +23,20 @@
|
||||||
"silent": true
|
"silent": true
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/lodash": "^4.14.116",
|
"@types/lodash": "latest",
|
||||||
"@types/react": "^16.4.8",
|
"@types/react": "latest",
|
||||||
"@types/react-native": "^0.56.6",
|
"@types/react-native": "latest",
|
||||||
"del": "^3.0.0",
|
"del": "latest",
|
||||||
"gulp": "^3.9.1",
|
"gulp": "latest",
|
||||||
"gulp-imagemin": "^4.1.0",
|
"gulp-imagemin": "latest",
|
||||||
"gulp-tslint": "^8.1.3",
|
"gulp-tslint": "latest",
|
||||||
"gulp-typescript": "^5.0.0-alpha.3",
|
"gulp-typescript": "latest",
|
||||||
"react-devtools": "latest",
|
"react-devtools": "latest",
|
||||||
"run-sequence": "^2.2.1",
|
"run-sequence": "latest",
|
||||||
"tslint": "^5.11.0",
|
"tslint": "latest",
|
||||||
"typescript": "^3.0.1"
|
"typescript": "latest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash": "^4.17.10"
|
"lodash": "latest"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,19 +25,23 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Touchable extends React.Component<IProps> {
|
export class Touchable extends React.Component<IProps> {
|
||||||
|
private testId: string;
|
||||||
|
|
||||||
constructor(props: IProps) {
|
constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
this.testId = this.props.testId + Guid.newGuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
DeviceEventEmitter.addListener('KeyEnter' + this.props.testId, this.props.onPress);
|
DeviceEventEmitter.addListener('KeyEnter' + this.testId, this.props.onPress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
DeviceEventEmitter.removeListener('KeyEnter' + this.props.testId, this.props.onPress);
|
DeviceEventEmitter.removeListener('KeyEnter' + this.testId, this.props.onPress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +66,7 @@ export class Touchable extends React.Component<IProps> {
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
onLongPress={onLongPress}
|
onLongPress={onLongPress}
|
||||||
accessible={true}
|
accessible={true}
|
||||||
testID={this.uniqueTestId}
|
testID={this.testId}
|
||||||
useForeground={true}
|
useForeground={true}
|
||||||
hitSlop={hitSlop}
|
hitSlop={hitSlop}
|
||||||
background={TouchableNativeFeedback.SelectableBackground()}
|
background={TouchableNativeFeedback.SelectableBackground()}
|
||||||
|
@ -81,7 +85,7 @@ export class Touchable extends React.Component<IProps> {
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
onLongPress={onLongPress}
|
onLongPress={onLongPress}
|
||||||
accessible={true}
|
accessible={true}
|
||||||
testID={this.uniqueTestId}
|
testID={this.testId}
|
||||||
activeOpacity={activeOpacity}
|
activeOpacity={activeOpacity}
|
||||||
style={style}
|
style={style}
|
||||||
hitSlop={hitSlop}
|
hitSlop={hitSlop}
|
||||||
|
@ -93,8 +97,4 @@ export class Touchable extends React.Component<IProps> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private get uniqueTestId() {
|
|
||||||
return this.props.testId + Guid.newGuid();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Text, View } from 'react-native';
|
||||||
|
import { StyleManager } from '../../Styles/StyleManager';
|
||||||
|
import { ImageBlock } from '../Basic/ImageBlock';
|
||||||
|
import { Touchable } from '../Basic/Touchable';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
avatar: string;
|
||||||
|
mainInfo: string;
|
||||||
|
subInfo: string;
|
||||||
|
hiddenFields: any;
|
||||||
|
theme: 'default' | 'emphasis';
|
||||||
|
onSelect?: (data: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Contact extends React.Component<IProps> {
|
||||||
|
public render() {
|
||||||
|
if (this.props.onSelect) {
|
||||||
|
return this.renderTouchableBlock();
|
||||||
|
} else {
|
||||||
|
return this.renderNonTouchableBlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderTouchableBlock() {
|
||||||
|
return (
|
||||||
|
<Touchable
|
||||||
|
onPress={this.onPress}
|
||||||
|
style={{
|
||||||
|
alignSelf: 'stretch',
|
||||||
|
flexDirection: 'row'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{this.renderContent()}
|
||||||
|
</Touchable>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderNonTouchableBlock() {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
alignSelf='stretch'
|
||||||
|
flexDirection='row'
|
||||||
|
>
|
||||||
|
{this.renderContent()}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderContent() {
|
||||||
|
return [
|
||||||
|
<ImageBlock
|
||||||
|
url={this.props.avatar}
|
||||||
|
mode='avatar'
|
||||||
|
width={StyleManager.getImageSize('medium') as number}
|
||||||
|
height={StyleManager.getImageSize('medium') as number}
|
||||||
|
/>,
|
||||||
|
<View>
|
||||||
|
<Text
|
||||||
|
accessible={true}
|
||||||
|
style={{
|
||||||
|
color: StyleManager.getColor('default', this.props.theme, false),
|
||||||
|
fontSize: StyleManager.getFontSize('default'),
|
||||||
|
fontWeight: StyleManager.getFontWeight('default'),
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
textAlign: StyleManager.getTextAlign('left'),
|
||||||
|
flexWrap: StyleManager.getWrap(true),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{this.props.mainInfo}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
accessible={true}
|
||||||
|
style={{
|
||||||
|
color: StyleManager.getColor('default', this.props.theme, true),
|
||||||
|
fontSize: StyleManager.getFontSize('small'),
|
||||||
|
fontWeight: StyleManager.getFontWeight('default'),
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
textAlign: StyleManager.getTextAlign('left'),
|
||||||
|
flexWrap: StyleManager.getWrap(true),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{this.props.subInfo}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private onPress = () => {
|
||||||
|
if (this.props.onSelect) {
|
||||||
|
this.props.onSelect(this.props.hiddenFields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -147,7 +147,7 @@ export class InputBox extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private get height() {
|
private get height() {
|
||||||
return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
|
return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get color() {
|
private get color() {
|
||||||
|
|
|
@ -0,0 +1,260 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import {
|
||||||
|
KeyboardTypeOptions,
|
||||||
|
ReturnKeyTypeOptions,
|
||||||
|
ScrollView,
|
||||||
|
StyleProp,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
TextStyle,
|
||||||
|
View
|
||||||
|
} from 'react-native';
|
||||||
|
import { StyleManager } from '../../Styles/StyleManager';
|
||||||
|
import { SeparateLine } from '../Basic/SeparateLine';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
placeholder: string;
|
||||||
|
value: string;
|
||||||
|
labels: Array<{ title: string, }>;
|
||||||
|
suggestionView: JSX.Element;
|
||||||
|
focused: boolean;
|
||||||
|
keyboardType?: KeyboardTypeOptions;
|
||||||
|
returnKeyType?: ReturnKeyTypeOptions;
|
||||||
|
numberOfLines?: number;
|
||||||
|
theme?: 'default' | 'emphasis';
|
||||||
|
flex?: number;
|
||||||
|
width?: number;
|
||||||
|
marginTop?: number;
|
||||||
|
marginBottom?: number;
|
||||||
|
marginLeft?: number;
|
||||||
|
marginRight?: number;
|
||||||
|
style?: StyleProp<TextStyle>;
|
||||||
|
onRequestSuggestion?: (input: string) => void;
|
||||||
|
onFocus?: () => void;
|
||||||
|
onBlur?: () => void;
|
||||||
|
validateInput?: (input: string) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
focused: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LabelInput extends React.Component<IProps, IState> {
|
||||||
|
private inputBox: TextInput;
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
focused: this.props.focused,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidUpdate(prevProps: IProps, prevState: IState) {
|
||||||
|
if (!prevState.focused && this.props.focused) {
|
||||||
|
this.setState({
|
||||||
|
focused: true,
|
||||||
|
}, () => {
|
||||||
|
if (this.inputBox) {
|
||||||
|
this.inputBox.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flex: this.props.flex,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{this.renderInputArea()}
|
||||||
|
{this.renderSuggestions()}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderInputArea() {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
alignSelf: 'stretch',
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
backgroundColor: this.backgroundColor,
|
||||||
|
borderColor: this.borderColor,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: 4,
|
||||||
|
width: this.props.width,
|
||||||
|
marginTop: this.props.marginTop,
|
||||||
|
marginRight: this.props.marginRight,
|
||||||
|
marginBottom: this.props.marginBottom,
|
||||||
|
marginLeft: this.props.marginLeft,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{this.renderLabels()}
|
||||||
|
{this.renderInputBox()}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderLabels() {
|
||||||
|
if (this.props.labels) {
|
||||||
|
return this.props.labels.map((label, index) => {
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
key={'Label' + index}
|
||||||
|
style={{
|
||||||
|
fontSize: this.fontSize,
|
||||||
|
fontWeight: this.fontWeight,
|
||||||
|
color: this.backgroundColor,
|
||||||
|
backgroundColor: this.color,
|
||||||
|
paddingTop: this.paddingVertical - 6,
|
||||||
|
paddingBottom: this.paddingVertical - 6,
|
||||||
|
borderRadius: 4,
|
||||||
|
paddingLeft: 6,
|
||||||
|
paddingRight: 6,
|
||||||
|
marginTop: 6,
|
||||||
|
marginBottom: 6,
|
||||||
|
marginLeft: 6,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label.title}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderInputBox() {
|
||||||
|
return (
|
||||||
|
<TextInput
|
||||||
|
ref={ref => this.inputBox = ref}
|
||||||
|
style={[
|
||||||
|
{
|
||||||
|
flex: 1,
|
||||||
|
color: this.color,
|
||||||
|
fontSize: this.fontSize,
|
||||||
|
fontWeight: this.fontWeight,
|
||||||
|
paddingTop: this.paddingVertical,
|
||||||
|
paddingRight: this.paddingHorizontal,
|
||||||
|
paddingBottom: this.paddingVertical,
|
||||||
|
paddingLeft: this.paddingHorizontal,
|
||||||
|
},
|
||||||
|
this.props.style
|
||||||
|
]}
|
||||||
|
multiline={this.isMultiLine}
|
||||||
|
numberOfLines={this.props.numberOfLines}
|
||||||
|
keyboardType={this.props.keyboardType}
|
||||||
|
blurOnSubmit={!this.isMultiLine}
|
||||||
|
placeholder={this.props.placeholder}
|
||||||
|
value={this.props.value}
|
||||||
|
returnKeyType={this.props.returnKeyType}
|
||||||
|
underlineColorAndroid={'transparent'}
|
||||||
|
importantForAccessibility={'no-hide-descendants'}
|
||||||
|
onChangeText={this.onValueChange}
|
||||||
|
onFocus={this.onFocus}
|
||||||
|
onBlur={this.onBlur}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderSuggestions() {
|
||||||
|
if (this.props.suggestionView) {
|
||||||
|
return [
|
||||||
|
<SeparateLine
|
||||||
|
key={0}
|
||||||
|
/>,
|
||||||
|
<ScrollView
|
||||||
|
key={1}
|
||||||
|
style={{
|
||||||
|
maxHeight: 200
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{this.props.suggestionView}
|
||||||
|
</ScrollView>
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private onValueChange = (value: string) => {
|
||||||
|
if (this.props.onRequestSuggestion) {
|
||||||
|
this.props.onRequestSuggestion(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onBlur = () => {
|
||||||
|
this.setState({ focused: false }, () => {
|
||||||
|
this.validateInput();
|
||||||
|
if (this.props.onBlur) {
|
||||||
|
this.props.onBlur();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private onFocus = () => {
|
||||||
|
this.setState({
|
||||||
|
focused: true
|
||||||
|
}, () => {
|
||||||
|
if (this.props.onFocus) {
|
||||||
|
this.props.onFocus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateInput() {
|
||||||
|
if (this.props.validateInput) {
|
||||||
|
if (this.props.validateInput(this.props.value)) {
|
||||||
|
console.log('Input: valid');
|
||||||
|
} else {
|
||||||
|
console.log('Input: invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private get isMultiLine() {
|
||||||
|
return this.props.numberOfLines && this.props.numberOfLines > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export class CardContext {
|
||||||
|
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ export interface FormField {
|
||||||
|
|
||||||
export class FormContext {
|
export class FormContext {
|
||||||
private formFields: { [id: string]: FormField } = {};
|
private formFields: { [id: string]: FormField } = {};
|
||||||
|
private fieldListeners: { [id: string]: Array<(value: string) => void> } = {};
|
||||||
|
|
||||||
private static sharedInstance: FormContext;
|
private static sharedInstance: FormContext;
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ export class FormContext {
|
||||||
value: value,
|
value: value,
|
||||||
validate: validate
|
validate: validate
|
||||||
};
|
};
|
||||||
|
this.getFieldListeners(id).forEach((listener) => listener(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,18 +69,6 @@ export class FormContext {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCallbackParamData(params: { [key: string]: string }): { [id: string]: string } {
|
|
||||||
if (params) {
|
|
||||||
return Object.keys(params).reduce((prev, current) => {
|
|
||||||
let formIndex = params[current];
|
|
||||||
console.log(formIndex);
|
|
||||||
prev[current] = this.getFieldValue(formIndex);
|
|
||||||
return prev;
|
|
||||||
}, {} as { [key: string]: string });
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
public validateField(id: string): boolean {
|
public validateField(id: string): boolean {
|
||||||
let field = this.getField(id);
|
let field = this.getField(id);
|
||||||
if (field) {
|
if (field) {
|
||||||
|
@ -95,4 +85,20 @@ export class FormContext {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public registerFieldListener(id: string, listener: (value: string) => void) {
|
||||||
|
if (!this.fieldListeners[id]) {
|
||||||
|
this.fieldListeners[id] = [listener];
|
||||||
|
} else {
|
||||||
|
this.fieldListeners[id].push(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFieldListeners(id: string) {
|
||||||
|
let result = this.fieldListeners[id];
|
||||||
|
if (!result) {
|
||||||
|
result = [];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { ConfigManager } from '../Config/ConfigManager';
|
||||||
import { HostConfig } from '../Config/Types';
|
import { HostConfig } from '../Config/Types';
|
||||||
import { ActionType } from '../Schema/Abstract/ActionElement';
|
import { ActionType } from '../Schema/Abstract/ActionElement';
|
||||||
import { OpenUrlActionElement } from '../Schema/Actions/OpenUrlAction';
|
import { OpenUrlActionElement } from '../Schema/Actions/OpenUrlAction';
|
||||||
|
import { SelectActionElement } from '../Schema/Actions/SelectAction';
|
||||||
import { ShowCardActionElement } from '../Schema/Actions/ShowCardAction';
|
import { ShowCardActionElement } from '../Schema/Actions/ShowCardAction';
|
||||||
import { SubmitActionElement } from '../Schema/Actions/SubmitAction';
|
import { SubmitActionElement } from '../Schema/Actions/SubmitAction';
|
||||||
import { IElement } from '../Schema/Interfaces/IElement';
|
import { IElement } from '../Schema/Interfaces/IElement';
|
||||||
|
@ -19,6 +20,7 @@ export class HostContext {
|
||||||
private onShowCard: (args?: ActionEventHandlerArgs<ShowCardActionElement>) => void;
|
private onShowCard: (args?: ActionEventHandlerArgs<ShowCardActionElement>) => void;
|
||||||
private onSubmit: (args?: ActionEventHandlerArgs<SubmitActionElement>) => void;
|
private onSubmit: (args?: ActionEventHandlerArgs<SubmitActionElement>) => void;
|
||||||
private onCallback: (args?: ActionEventHandlerArgs<CallbackAction>) => void;
|
private onCallback: (args?: ActionEventHandlerArgs<CallbackAction>) => void;
|
||||||
|
private onSelectAction: (args?: ActionEventHandlerArgs<SelectActionElement>) => void;
|
||||||
|
|
||||||
private static sharedInstance: HostContext;
|
private static sharedInstance: HostContext;
|
||||||
|
|
||||||
|
@ -69,6 +71,10 @@ export class HostContext {
|
||||||
this.onCallback = handler;
|
this.onCallback = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public registerSelectActionHandler(handler: (args?: ActionEventHandlerArgs<SelectActionElement>) => void) {
|
||||||
|
this.onSelectAction = handler;
|
||||||
|
}
|
||||||
|
|
||||||
public applyConfig(configJson: any) {
|
public applyConfig(configJson: any) {
|
||||||
this.config.combine(new HostConfig(configJson));
|
this.config.combine(new HostConfig(configJson));
|
||||||
}
|
}
|
||||||
|
@ -92,6 +98,9 @@ export class HostContext {
|
||||||
case ActionType.Submit:
|
case ActionType.Submit:
|
||||||
callback = this.onSubmit;
|
callback = this.onSubmit;
|
||||||
break;
|
break;
|
||||||
|
case ActionType.Select:
|
||||||
|
callback = this.onSelectAction;
|
||||||
|
break;
|
||||||
case 'focus':
|
case 'focus':
|
||||||
callback = this.onFocus;
|
callback = this.onFocus;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
import { Image } from 'react-native';
|
|
||||||
import { Dimension } from '../Shared/Types';
|
|
||||||
|
|
||||||
export class MediaContext {
|
|
||||||
private mediaSizes: { [url: string]: Dimension } = {};
|
|
||||||
private static sharedInstance: MediaContext;
|
|
||||||
|
|
||||||
private constructor() { }
|
|
||||||
|
|
||||||
public static getInstance() {
|
|
||||||
if (MediaContext.sharedInstance === undefined) {
|
|
||||||
MediaContext.sharedInstance = new MediaContext();
|
|
||||||
}
|
|
||||||
return MediaContext.sharedInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public fetchImageSize(url: string, onSuccess: (width: number, height: number) => void, onFailure: (error: any) => void) {
|
|
||||||
let cache = this.getSize(url);
|
|
||||||
if (cache) {
|
|
||||||
onSuccess(cache.width, cache.height);
|
|
||||||
} else {
|
|
||||||
Image.getSize(
|
|
||||||
url,
|
|
||||||
(width, height) => {
|
|
||||||
this.cacheSize(url, { width: width, height: height });
|
|
||||||
onSuccess(width, height);
|
|
||||||
},
|
|
||||||
onFailure
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public cacheSize(url: string, size: Dimension) {
|
|
||||||
this.mediaSizes[url] = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSize(url: string) {
|
|
||||||
return this.mediaSizes[url];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,6 +5,7 @@ import { AbstractElement } from './AbstractElement';
|
||||||
|
|
||||||
export enum ActionType {
|
export enum ActionType {
|
||||||
OpenUrl = 'Action.OpenUrl',
|
OpenUrl = 'Action.OpenUrl',
|
||||||
|
Select = 'Action.Select',
|
||||||
Submit = 'Action.Submit',
|
Submit = 'Action.Submit',
|
||||||
ShowCard = 'Action.ShowCard',
|
ShowCard = 'Action.ShowCard',
|
||||||
Callback = 'Action.Callback',
|
Callback = 'Action.Callback',
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { ElementUtils } from '../../Utils/ElementUtils';
|
||||||
|
import { ActionElement } from '../Abstract/ActionElement';
|
||||||
|
import { IElement } from '../Interfaces/IElement';
|
||||||
|
import { IInput } from '../Interfaces/IInput';
|
||||||
|
import { IScope } from '../Interfaces/IScope';
|
||||||
|
|
||||||
|
export class SelectActionElement extends ActionElement {
|
||||||
|
// Required
|
||||||
|
public readonly title: string;
|
||||||
|
public readonly subTitle: string;
|
||||||
|
public readonly data: any;
|
||||||
|
public readonly children: IElement[] = [];
|
||||||
|
|
||||||
|
constructor(json: any, parent: IElement) {
|
||||||
|
super(json, parent);
|
||||||
|
|
||||||
|
if (this.isValid) {
|
||||||
|
this.title = json.selectedTextTitle;
|
||||||
|
this.subTitle = json.selectedTextSubTitle;
|
||||||
|
this.data = json.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get targetFormField() {
|
||||||
|
let targetInput = this.ancestorsAndSelf.find(element => ElementUtils.isSelectActionTarget(element.type)) as IInput;
|
||||||
|
if (targetInput) {
|
||||||
|
return targetInput.id;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get scope(): IScope {
|
||||||
|
return this.ancestorsAndSelf.find(element => element.parent === undefined) as IScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get requiredProperties() {
|
||||||
|
return ['type', 'selectedTextTitle', 'selectedTextSubTitle', 'data'];
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ export class CardElement extends ScopeElement {
|
||||||
public readonly actions?: IAction[];
|
public readonly actions?: IAction[];
|
||||||
public readonly body?: IContent[];
|
public readonly body?: IContent[];
|
||||||
public readonly backgroundImage?: string;
|
public readonly backgroundImage?: string;
|
||||||
|
public readonly cardSource?: string;
|
||||||
|
|
||||||
constructor(json: any, parent: IElement) {
|
constructor(json: any, parent: IElement) {
|
||||||
super(json, parent);
|
super(json, parent);
|
||||||
|
|
|
@ -11,6 +11,7 @@ export class ColumnSetElement extends ScopeElement {
|
||||||
|
|
||||||
if (this.isValid) {
|
if (this.isValid) {
|
||||||
this.columns = [];
|
this.columns = [];
|
||||||
|
|
||||||
if (json.columns) {
|
if (json.columns) {
|
||||||
json.columns.forEach((item: any) => {
|
json.columns.forEach((item: any) => {
|
||||||
let column: ColumnElement = new ColumnElement(item, this);
|
let column: ColumnElement = new ColumnElement(item, this);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { ActionElement, ActionType } from '../Abstract/ActionElement';
|
import { ActionElement, ActionType } from '../Abstract/ActionElement';
|
||||||
import { OpenUrlActionElement } from '../Actions/OpenUrlAction';
|
import { OpenUrlActionElement } from '../Actions/OpenUrlAction';
|
||||||
|
import { SelectActionElement } from '../Actions/SelectAction';
|
||||||
import { ShowCardActionElement } from '../Actions/ShowCardAction';
|
import { ShowCardActionElement } from '../Actions/ShowCardAction';
|
||||||
import { SubmitActionElement } from '../Actions/SubmitAction';
|
import { SubmitActionElement } from '../Actions/SubmitAction';
|
||||||
import { IAction } from '../Interfaces/IAction';
|
import { IAction } from '../Interfaces/IAction';
|
||||||
|
@ -23,6 +24,9 @@ export class ActionFactory {
|
||||||
case ActionType.ShowCard:
|
case ActionType.ShowCard:
|
||||||
action = new ShowCardActionElement(json, parent);
|
action = new ShowCardActionElement(json, parent);
|
||||||
break;
|
break;
|
||||||
|
case ActionType.Select:
|
||||||
|
action = new SelectActionElement(json, parent);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
action = undefined;
|
action = undefined;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { IAction } from './IAction';
|
||||||
import { IContent } from './IContent';
|
import { IContent } from './IContent';
|
||||||
|
|
||||||
export interface IScope extends IContent {
|
export interface IScope extends IContent {
|
||||||
readonly selectAction?: IContent;
|
readonly selectAction?: IAction;
|
||||||
readonly backgroundImage?: string | { url: string };
|
readonly backgroundImage?: string | { url: string };
|
||||||
readonly action: IAction;
|
readonly action: IAction;
|
||||||
readonly inputFields: string[];
|
readonly inputFields: string[];
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export class ElementUtils {
|
export class ElementUtils {
|
||||||
private static inputTypes = ['Input.Text', 'Input.Number', 'Input.Date', 'Input.Time', 'Input.Toggle', 'Input.ChoiceSet'];
|
private static inputTypes = ['Input.Text', 'Input.Number', 'Input.Date', 'Input.Time', 'Input.Toggle', 'Input.ChoiceSet'];
|
||||||
private static valueTypes = ['Fact', 'Input.Choice'];
|
private static valueTypes = ['Fact', 'Input.Choice'];
|
||||||
|
private static selectActionTargetTypes = ['Input.PeoplePicker'];
|
||||||
|
|
||||||
public static isInput(type: string) {
|
public static isInput(type: string) {
|
||||||
return ElementUtils.inputTypes.indexOf(type) >= 0;
|
return ElementUtils.inputTypes.indexOf(type) >= 0;
|
||||||
|
@ -9,4 +10,8 @@ export class ElementUtils {
|
||||||
public static isValue(type: string) {
|
public static isValue(type: string) {
|
||||||
return ElementUtils.valueTypes.indexOf(type) >= 0;
|
return ElementUtils.valueTypes.indexOf(type) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static isSelectActionTarget(type: string) {
|
||||||
|
return ElementUtils.selectActionTargetTypes.indexOf(type) >= 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { FactSetElement } from '../../Schema/Containers/FactSet';
|
||||||
import { ImageSetElement } from '../../Schema/Containers/ImageSet';
|
import { ImageSetElement } from '../../Schema/Containers/ImageSet';
|
||||||
import { DateInputElement } from '../../Schema/Inputs/DateInput';
|
import { DateInputElement } from '../../Schema/Inputs/DateInput';
|
||||||
import { NumberInputElement } from '../../Schema/Inputs/NumberInput';
|
import { NumberInputElement } from '../../Schema/Inputs/NumberInput';
|
||||||
|
import { PeoplePickerElement } from '../../Schema/Inputs/PeoplePicker';
|
||||||
import { TextInputElement } from '../../Schema/Inputs/TextInput';
|
import { TextInputElement } from '../../Schema/Inputs/TextInput';
|
||||||
import { TimeInputElement } from '../../Schema/Inputs/TimeInput';
|
import { TimeInputElement } from '../../Schema/Inputs/TimeInput';
|
||||||
import { ImageView } from '../CardElements/Image';
|
import { ImageView } from '../CardElements/Image';
|
||||||
|
@ -22,6 +23,7 @@ import { FactSetView } from '../Containers/FactSet';
|
||||||
import { ImageSetView } from '../Containers/ImageSet';
|
import { ImageSetView } from '../Containers/ImageSet';
|
||||||
import { DateInputView } from '../Inputs/DateInput';
|
import { DateInputView } from '../Inputs/DateInput';
|
||||||
import { NumberInputView } from '../Inputs/NumberInput';
|
import { NumberInputView } from '../Inputs/NumberInput';
|
||||||
|
import { PeoplePickerView } from '../Inputs/PeoplePicker';
|
||||||
import { TextInputView } from '../Inputs/TextInput';
|
import { TextInputView } from '../Inputs/TextInput';
|
||||||
import { TimeInputView } from '../Inputs/TimeInput';
|
import { TimeInputView } from '../Inputs/TimeInput';
|
||||||
|
|
||||||
|
@ -160,6 +162,15 @@ export class ContentFactory {
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
case ContentElementType.PeoplePicker:
|
||||||
|
return (
|
||||||
|
<PeoplePickerView
|
||||||
|
key={'PeoplePickerView' + index}
|
||||||
|
element={element as PeoplePickerElement}
|
||||||
|
index={index}
|
||||||
|
theme={theme}
|
||||||
|
/>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,7 @@ export class DateInputView extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private get height() {
|
private get height() {
|
||||||
return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
|
return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get color() {
|
private get color() {
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { LabelInput } from '../../Components/Inputs/LabelInput';
|
||||||
|
import { ActionContext } from '../../Contexts/ActionContext';
|
||||||
|
import { FormContext } from '../../Contexts/FormContext';
|
||||||
|
import { ActionType } from '../../Schema/Abstract/ActionElement';
|
||||||
|
import { CardElement } from '../../Schema/Cards/Card';
|
||||||
|
import { PeoplePickerElement } from '../../Schema/Inputs/PeoplePicker';
|
||||||
|
import { CallbackAction } from '../../Schema/Internal/CallbackAction';
|
||||||
|
import { ActionEventHandlerArgs } from '../../Shared/Types';
|
||||||
|
import { ContentFactory } from '../Factories/ContentFactory';
|
||||||
|
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
index: number;
|
||||||
|
element: PeoplePickerElement;
|
||||||
|
theme: 'default' | 'emphasis';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
value: string;
|
||||||
|
selected: Array<{ Name: string, Address: string }>;
|
||||||
|
inputFocused: boolean;
|
||||||
|
suggestionCard: CardElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PeoplePickerView extends React.Component<IProps, IState> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const { element } = this.props;
|
||||||
|
|
||||||
|
if (element && element.isValid) {
|
||||||
|
this.state = {
|
||||||
|
value: '',
|
||||||
|
inputFocused: false,
|
||||||
|
selected: [],
|
||||||
|
suggestionCard: undefined,
|
||||||
|
};
|
||||||
|
FormContext.getInstance().updateField(element.id, JSON.stringify([]), true);
|
||||||
|
FormContext.getInstance().registerFieldListener(element.id, this.storeListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { element, theme } = this.props;
|
||||||
|
|
||||||
|
if (!element || !element.isValid) {
|
||||||
|
return DebugOutputFactory.createDebugOutputBanner(element.type + '>>' + element.id + ' is not valid', theme, 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LabelInput
|
||||||
|
placeholder={element.placeholder}
|
||||||
|
value={this.state.value}
|
||||||
|
focused={this.state.inputFocused}
|
||||||
|
labels={this.labels}
|
||||||
|
suggestionView={ContentFactory.createElement(this.state.suggestionCard, 0, theme)}
|
||||||
|
onRequestSuggestion={this.onRequestSuggestion}
|
||||||
|
onFocus={this.onFocus}
|
||||||
|
onBlur={this.onBlur}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onBlur = () => {
|
||||||
|
this.setState({ inputFocused: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
private onFocus = () => {
|
||||||
|
this.setState({ inputFocused: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSuggestionCallback = (data: any) => {
|
||||||
|
this.setState({
|
||||||
|
suggestionCard: new CardElement(data, this.props.element),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private onRequestSuggestion = (input: string) => {
|
||||||
|
this.setState({
|
||||||
|
value: input,
|
||||||
|
}, () => {
|
||||||
|
const { element } = this.props;
|
||||||
|
|
||||||
|
if (element.callback) {
|
||||||
|
let callback = ActionContext.getGlobalInstance().getActionEventHandler(element.callback, this.onSuggestionCallback);
|
||||||
|
if (callback) {
|
||||||
|
callback({
|
||||||
|
actionType: ActionType.Callback,
|
||||||
|
func: this.populateApiParams,
|
||||||
|
name: 'populateApiParams'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private populateApiParams = (args: ActionEventHandlerArgs<CallbackAction>) => {
|
||||||
|
if (args && args.formValidate) {
|
||||||
|
args.formData = this.extractParamData();
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
private extractParamData = () => {
|
||||||
|
const { element } = this.props;
|
||||||
|
|
||||||
|
if (element.callback) {
|
||||||
|
const params = element.callback.parameters;
|
||||||
|
if (params) {
|
||||||
|
return Object.keys(params).reduce((prev, current) => {
|
||||||
|
let formIndex = params[current];
|
||||||
|
if (formIndex === element.id) {
|
||||||
|
prev[current] = this.state.value;
|
||||||
|
} else {
|
||||||
|
prev[current] = FormContext.getInstance().getFieldValue(formIndex);
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}, {} as { [key: string]: string });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
private storeListener = (value: string) => {
|
||||||
|
this.setState({
|
||||||
|
selected: JSON.parse(value),
|
||||||
|
suggestionCard: undefined,
|
||||||
|
inputFocused: true,
|
||||||
|
value: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private get labels() {
|
||||||
|
return this.state.selected.map((contact) => {
|
||||||
|
return {
|
||||||
|
title: contact.Name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -137,7 +137,7 @@ export class TimeInputView extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private get height() {
|
private get height() {
|
||||||
return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
|
return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get color() {
|
private get color() {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { FormContext } from '../Contexts/FormContext';
|
||||||
import { HostContext } from '../Contexts/HostContext';
|
import { HostContext } from '../Contexts/HostContext';
|
||||||
import { ActionType } from '../Schema/Abstract/ActionElement';
|
import { ActionType } from '../Schema/Abstract/ActionElement';
|
||||||
import { OpenUrlActionElement } from '../Schema/Actions/OpenUrlAction';
|
import { OpenUrlActionElement } from '../Schema/Actions/OpenUrlAction';
|
||||||
|
import { SelectActionElement } from '../Schema/Actions/SelectAction';
|
||||||
import { SubmitActionElement } from '../Schema/Actions/SubmitAction';
|
import { SubmitActionElement } from '../Schema/Actions/SubmitAction';
|
||||||
import { CardElement } from '../Schema/Cards/Card';
|
import { CardElement } from '../Schema/Cards/Card';
|
||||||
import { CallbackAction } from '../Schema/Internal/CallbackAction';
|
import { CallbackAction } from '../Schema/Internal/CallbackAction';
|
||||||
|
@ -47,6 +48,7 @@ export class CardRootView extends React.PureComponent<IAdaptiveCardProps> {
|
||||||
hostContext.registerOpenUrlHandler(this.onOpenUrl);
|
hostContext.registerOpenUrlHandler(this.onOpenUrl);
|
||||||
hostContext.registerSubmitHandler(this.onSubmit);
|
hostContext.registerSubmitHandler(this.onSubmit);
|
||||||
hostContext.registerCallbackHandler(this.onCallback);
|
hostContext.registerCallbackHandler(this.onCallback);
|
||||||
|
hostContext.registerSelectActionHandler(this.onSelectAction);
|
||||||
|
|
||||||
hostContext.registerFocusHandler(this.props.onFocus);
|
hostContext.registerFocusHandler(this.props.onFocus);
|
||||||
hostContext.registerBlurHandler(this.props.onBlur);
|
hostContext.registerBlurHandler(this.props.onBlur);
|
||||||
|
@ -76,9 +78,9 @@ export class CardRootView extends React.PureComponent<IAdaptiveCardProps> {
|
||||||
});
|
});
|
||||||
|
|
||||||
actionContext.registerHook({
|
actionContext.registerHook({
|
||||||
func: this.populateCallbackParamData,
|
func: this.populateSelectActionData,
|
||||||
name: 'populateCallbackParamData',
|
name: 'populateSelectActionData',
|
||||||
actionType: ActionType.Callback
|
actionType: ActionType.Select
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,6 +142,16 @@ export class CardRootView extends React.PureComponent<IAdaptiveCardProps> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onSelectAction = (args: ActionEventHandlerArgs<SelectActionElement>) => {
|
||||||
|
if (args) {
|
||||||
|
let currentValue = JSON.parse(FormContext.getInstance().getFieldValue(args.action.targetFormField)) as Array<any>;
|
||||||
|
if (currentValue) {
|
||||||
|
currentValue.push(args.formData);
|
||||||
|
FormContext.getInstance().updateField(args.action.targetFormField, JSON.stringify(currentValue), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private validateForm = (args: ActionEventHandlerArgs<SubmitActionElement>) => {
|
private validateForm = (args: ActionEventHandlerArgs<SubmitActionElement>) => {
|
||||||
if (args) {
|
if (args) {
|
||||||
args.formValidate = args.action.scope.validateScope();
|
args.formValidate = args.action.scope.validateScope();
|
||||||
|
@ -164,9 +176,11 @@ export class CardRootView extends React.PureComponent<IAdaptiveCardProps> {
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
private populateCallbackParamData = (args: ActionEventHandlerArgs<CallbackAction>) => {
|
private populateSelectActionData = (args: ActionEventHandlerArgs<SelectActionElement>) => {
|
||||||
if (args && args.formValidate) {
|
if (args) {
|
||||||
args.formData = FormContext.getInstance().getCallbackParamData(args.action.parameters);
|
args.formData = {
|
||||||
|
...(args.action.data || {}),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
35
yarn.lock
35
yarn.lock
|
@ -6,7 +6,7 @@
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/fancy-log/-/fancy-log-1.3.0.tgz#a61ab476e5e628cd07a846330df53b85e05c8ce0"
|
resolved "https://registry.yarnpkg.com/@types/fancy-log/-/fancy-log-1.3.0.tgz#a61ab476e5e628cd07a846330df53b85e05c8ce0"
|
||||||
|
|
||||||
"@types/lodash@^4.14.116":
|
"@types/lodash@latest":
|
||||||
version "4.14.116"
|
version "4.14.116"
|
||||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.116.tgz#5ccf215653e3e8c786a58390751033a9adca0eb9"
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.116.tgz#5ccf215653e3e8c786a58390751033a9adca0eb9"
|
||||||
|
|
||||||
|
@ -20,19 +20,26 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react-native@^0.56.6":
|
"@types/react-native@latest":
|
||||||
version "0.56.6"
|
version "0.56.9"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.56.6.tgz#d9c60e1df95ec0402fa956225558c8a48f37f23f"
|
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.56.9.tgz#87e5877b98aed2b1dcd1def6f69927b24c2bd7e5"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react@*", "@types/react@^16.4.8":
|
"@types/react@*":
|
||||||
version "16.4.8"
|
version "16.4.8"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.8.tgz#ff0440429783df0927bdcd430fa1225f7c08cf36"
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.8.tgz#ff0440429783df0927bdcd430fa1225f7c08cf36"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/prop-types" "*"
|
"@types/prop-types" "*"
|
||||||
csstype "^2.2.0"
|
csstype "^2.2.0"
|
||||||
|
|
||||||
|
"@types/react@latest":
|
||||||
|
version "16.4.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.11.tgz#330f3d864300f71150dc2d125e48644c098f8770"
|
||||||
|
dependencies:
|
||||||
|
"@types/prop-types" "*"
|
||||||
|
csstype "^2.2.0"
|
||||||
|
|
||||||
ajv@^5.1.0:
|
ajv@^5.1.0:
|
||||||
version "5.5.2"
|
version "5.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
|
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
|
||||||
|
@ -825,7 +832,7 @@ define-property@^2.0.2:
|
||||||
is-descriptor "^1.0.2"
|
is-descriptor "^1.0.2"
|
||||||
isobject "^3.0.1"
|
isobject "^3.0.1"
|
||||||
|
|
||||||
del@^3.0.0:
|
del@latest:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
|
resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1598,7 +1605,7 @@ gulp-decompress@^1.2.0:
|
||||||
gulp-util "^3.0.1"
|
gulp-util "^3.0.1"
|
||||||
readable-stream "^2.0.2"
|
readable-stream "^2.0.2"
|
||||||
|
|
||||||
gulp-imagemin@^4.1.0:
|
gulp-imagemin@latest:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/gulp-imagemin/-/gulp-imagemin-4.1.0.tgz#5ce347f1d1706fed3cc8f1777ca9094a583b50b7"
|
resolved "https://registry.yarnpkg.com/gulp-imagemin/-/gulp-imagemin-4.1.0.tgz#5ce347f1d1706fed3cc8f1777ca9094a583b50b7"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1629,7 +1636,7 @@ gulp-sourcemaps@1.6.0:
|
||||||
through2 "^2.0.0"
|
through2 "^2.0.0"
|
||||||
vinyl "^1.0.0"
|
vinyl "^1.0.0"
|
||||||
|
|
||||||
gulp-tslint@^8.1.3:
|
gulp-tslint@latest:
|
||||||
version "8.1.3"
|
version "8.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/gulp-tslint/-/gulp-tslint-8.1.3.tgz#a89ed144038ae861ee7bfea9528272d126a93da1"
|
resolved "https://registry.yarnpkg.com/gulp-tslint/-/gulp-tslint-8.1.3.tgz#a89ed144038ae861ee7bfea9528272d126a93da1"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1640,7 +1647,7 @@ gulp-tslint@^8.1.3:
|
||||||
plugin-error "1.0.1"
|
plugin-error "1.0.1"
|
||||||
through "~2.3.8"
|
through "~2.3.8"
|
||||||
|
|
||||||
gulp-typescript@^5.0.0-alpha.3:
|
gulp-typescript@latest:
|
||||||
version "5.0.0-alpha.3"
|
version "5.0.0-alpha.3"
|
||||||
resolved "https://registry.yarnpkg.com/gulp-typescript/-/gulp-typescript-5.0.0-alpha.3.tgz#c49a306cbbb8c97f5fe8a79208671b6642ef9861"
|
resolved "https://registry.yarnpkg.com/gulp-typescript/-/gulp-typescript-5.0.0-alpha.3.tgz#c49a306cbbb8c97f5fe8a79208671b6642ef9861"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1674,7 +1681,7 @@ gulp-util@^3.0.0, gulp-util@^3.0.1:
|
||||||
through2 "^2.0.0"
|
through2 "^2.0.0"
|
||||||
vinyl "^0.5.0"
|
vinyl "^0.5.0"
|
||||||
|
|
||||||
gulp@^3.9.1:
|
gulp@latest:
|
||||||
version "3.9.1"
|
version "3.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/gulp/-/gulp-3.9.1.tgz#571ce45928dd40af6514fc4011866016c13845b4"
|
resolved "https://registry.yarnpkg.com/gulp/-/gulp-3.9.1.tgz#571ce45928dd40af6514fc4011866016c13845b4"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -2437,7 +2444,7 @@ lodash.templatesettings@^3.0.0:
|
||||||
lodash._reinterpolate "^3.0.0"
|
lodash._reinterpolate "^3.0.0"
|
||||||
lodash.escape "^3.0.0"
|
lodash.escape "^3.0.0"
|
||||||
|
|
||||||
lodash@^4.17.10:
|
lodash@latest:
|
||||||
version "4.17.10"
|
version "4.17.10"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
|
||||||
|
|
||||||
|
@ -3309,7 +3316,7 @@ rimraf@^2.2.6, rimraf@^2.2.8, rimraf@^2.5.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
glob "^7.0.5"
|
glob "^7.0.5"
|
||||||
|
|
||||||
run-sequence@^2.2.1:
|
run-sequence@latest:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/run-sequence/-/run-sequence-2.2.1.tgz#1ce643da36fd8c7ea7e1a9329da33fc2b8898495"
|
resolved "https://registry.yarnpkg.com/run-sequence/-/run-sequence-2.2.1.tgz#1ce643da36fd8c7ea7e1a9329da33fc2b8898495"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -3857,7 +3864,7 @@ tslib@^1.8.0, tslib@^1.8.1:
|
||||||
version "1.9.3"
|
version "1.9.3"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
|
||||||
|
|
||||||
tslint@^5.11.0:
|
tslint@latest:
|
||||||
version "5.11.0"
|
version "5.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed"
|
resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -3898,7 +3905,7 @@ typedarray@^0.0.6:
|
||||||
version "0.0.6"
|
version "0.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
|
|
||||||
typescript@^3.0.1:
|
typescript@latest:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.1.tgz#43738f29585d3a87575520a4b93ab6026ef11fdb"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.1.tgz#43738f29585d3a87575520a4b93ab6026ef11fdb"
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче