diff --git a/dist/Components/Basic/Touchable.js b/dist/Components/Basic/Touchable.js
index e07d743..247c2db 100644
--- a/dist/Components/Basic/Touchable.js
+++ b/dist/Components/Basic/Touchable.js
@@ -13,28 +13,26 @@ import { Guid } from '../../Shared/Guid';
export class Touchable extends React.Component {
constructor(props) {
super(props);
+ this.testId = this.props.testId + Guid.newGuid();
}
componentDidMount() {
if (Platform.OS === 'android') {
- DeviceEventEmitter.addListener('KeyEnter' + this.props.testId, this.props.onPress);
+ DeviceEventEmitter.addListener('KeyEnter' + this.testId, this.props.onPress);
}
}
componentWillUnmount() {
if (Platform.OS === 'android') {
- DeviceEventEmitter.removeListener('KeyEnter' + this.props.testId, this.props.onPress);
+ DeviceEventEmitter.removeListener('KeyEnter' + this.testId, this.props.onPress);
}
}
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"]);
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))));
}
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();
- }
}
diff --git a/dist/Components/Inputs/Contact.js b/dist/Components/Inputs/Contact.js
new file mode 100644
index 0000000..2527762
--- /dev/null
+++ b/dist/Components/Inputs/Contact.js
@@ -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))
+ ];
+ }
+}
diff --git a/dist/Components/Inputs/InputBox.js b/dist/Components/Inputs/InputBox.js
index c3bacc1..3c5aa10 100644
--- a/dist/Components/Inputs/InputBox.js
+++ b/dist/Components/Inputs/InputBox.js
@@ -87,7 +87,7 @@ export class InputBox extends React.Component {
return 1;
}
get height() {
- return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
+ return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
}
get color() {
if (this.state.focused) {
diff --git a/dist/Components/Inputs/LabelInput.js b/dist/Components/Inputs/LabelInput.js
new file mode 100644
index 0000000..69b0b49
--- /dev/null
+++ b/dist/Components/Inputs/LabelInput.js
@@ -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);
+ }
+ }
+}
diff --git a/dist/Contexts/CardContext.js b/dist/Contexts/CardContext.js
new file mode 100644
index 0000000..dbec3aa
--- /dev/null
+++ b/dist/Contexts/CardContext.js
@@ -0,0 +1,2 @@
+export class CardContext {
+}
diff --git a/dist/Contexts/FormContext.js b/dist/Contexts/FormContext.js
index 899af6f..abfd6bb 100644
--- a/dist/Contexts/FormContext.js
+++ b/dist/Contexts/FormContext.js
@@ -1,6 +1,7 @@
export class FormContext {
constructor() {
this.formFields = {};
+ this.fieldListeners = {};
}
static getInstance() {
if (FormContext.sharedInstance === undefined) {
@@ -14,6 +15,7 @@ export class FormContext {
value: value,
validate: validate
};
+ this.getFieldListeners(id).forEach((listener) => listener(value));
}
}
getField(id) {
@@ -54,17 +56,6 @@ export class FormContext {
}
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) {
let field = this.getField(id);
if (field) {
@@ -80,4 +71,19 @@ export class FormContext {
}
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;
+ }
}
diff --git a/dist/Contexts/HostContext.js b/dist/Contexts/HostContext.js
index 8d9e86d..197bcbf 100644
--- a/dist/Contexts/HostContext.js
+++ b/dist/Contexts/HostContext.js
@@ -38,6 +38,9 @@ export class HostContext {
registerCallbackHandler(handler) {
this.onCallback = handler;
}
+ registerSelectActionHandler(handler) {
+ this.onSelectAction = handler;
+ }
applyConfig(configJson) {
this.config.combine(new HostConfig(configJson));
}
@@ -59,6 +62,9 @@ export class HostContext {
case ActionType.Submit:
callback = this.onSubmit;
break;
+ case ActionType.Select:
+ callback = this.onSelectAction;
+ break;
case 'focus':
callback = this.onFocus;
break;
diff --git a/dist/Contexts/MediaContext.js b/dist/Contexts/MediaContext.js
deleted file mode 100644
index 8801ef1..0000000
--- a/dist/Contexts/MediaContext.js
+++ /dev/null
@@ -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];
- }
-}
diff --git a/dist/Schema/Abstract/ActionElement.js b/dist/Schema/Abstract/ActionElement.js
index 855ef06..d6b3bf7 100644
--- a/dist/Schema/Abstract/ActionElement.js
+++ b/dist/Schema/Abstract/ActionElement.js
@@ -2,6 +2,7 @@ import { AbstractElement } from './AbstractElement';
export var ActionType;
(function (ActionType) {
ActionType["OpenUrl"] = "Action.OpenUrl";
+ ActionType["Select"] = "Action.Select";
ActionType["Submit"] = "Action.Submit";
ActionType["ShowCard"] = "Action.ShowCard";
ActionType["Callback"] = "Action.Callback";
diff --git a/dist/Schema/Actions/SelectAction.js b/dist/Schema/Actions/SelectAction.js
new file mode 100644
index 0000000..7259dfc
--- /dev/null
+++ b/dist/Schema/Actions/SelectAction.js
@@ -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'];
+ }
+}
diff --git a/dist/Schema/Factories/ActionFactory.js b/dist/Schema/Factories/ActionFactory.js
index 9cc9ebd..2937d44 100644
--- a/dist/Schema/Factories/ActionFactory.js
+++ b/dist/Schema/Factories/ActionFactory.js
@@ -1,5 +1,6 @@
import { ActionType } from '../Abstract/ActionElement';
import { OpenUrlActionElement } from '../Actions/OpenUrlAction';
+import { SelectActionElement } from '../Actions/SelectAction';
import { ShowCardActionElement } from '../Actions/ShowCardAction';
import { SubmitActionElement } from '../Actions/SubmitAction';
export class ActionFactory {
@@ -18,6 +19,9 @@ export class ActionFactory {
case ActionType.ShowCard:
action = new ShowCardActionElement(json, parent);
break;
+ case ActionType.Select:
+ action = new SelectActionElement(json, parent);
+ break;
default:
action = undefined;
break;
diff --git a/dist/Utils/ElementUtils.js b/dist/Utils/ElementUtils.js
index 92a53d9..d0429f9 100644
--- a/dist/Utils/ElementUtils.js
+++ b/dist/Utils/ElementUtils.js
@@ -5,6 +5,10 @@ export class ElementUtils {
static isValue(type) {
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.valueTypes = ['Fact', 'Input.Choice'];
+ElementUtils.selectActionTargetTypes = ['Input.PeoplePicker'];
diff --git a/dist/Views/Factories/ContentFactory.js b/dist/Views/Factories/ContentFactory.js
index a63e152..2f8d9b8 100644
--- a/dist/Views/Factories/ContentFactory.js
+++ b/dist/Views/Factories/ContentFactory.js
@@ -11,6 +11,7 @@ import { FactSetView } from '../Containers/FactSet';
import { ImageSetView } from '../Containers/ImageSet';
import { DateInputView } from '../Inputs/DateInput';
import { NumberInputView } from '../Inputs/NumberInput';
+import { PeoplePickerView } from '../Inputs/PeoplePicker';
import { TextInputView } from '../Inputs/TextInput';
import { TimeInputView } from '../Inputs/TimeInput';
export class ContentFactory {
@@ -61,6 +62,8 @@ export class ContentFactory {
return (React.createElement(DateInputView, { key: 'DateInputView' + index, element: element, index: index, theme: theme }));
case ContentElementType.TimeInput:
return (React.createElement(TimeInputView, { key: 'TimeInputView' + index, element: element, index: index, theme: theme }));
+ case ContentElementType.PeoplePicker:
+ return (React.createElement(PeoplePickerView, { key: 'PeoplePickerView' + index, element: element, index: index, theme: theme }));
default:
return null;
}
diff --git a/dist/Views/Inputs/DateInput.js b/dist/Views/Inputs/DateInput.js
index 9346dd5..ec8a3db 100644
--- a/dist/Views/Inputs/DateInput.js
+++ b/dist/Views/Inputs/DateInput.js
@@ -75,7 +75,7 @@ export class DateInputView extends React.Component {
return 1;
}
get height() {
- return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
+ return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
}
get color() {
if (this.state.focused) {
diff --git a/dist/Views/Inputs/PeoplePicker.js b/dist/Views/Inputs/PeoplePicker.js
new file mode 100644
index 0000000..1c710d7
--- /dev/null
+++ b/dist/Views/Inputs/PeoplePicker.js
@@ -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
+ };
+ });
+ }
+}
diff --git a/dist/Views/Inputs/TimeInput.js b/dist/Views/Inputs/TimeInput.js
index fb47946..28be876 100644
--- a/dist/Views/Inputs/TimeInput.js
+++ b/dist/Views/Inputs/TimeInput.js
@@ -75,7 +75,7 @@ export class TimeInputView extends React.Component {
return 1;
}
get height() {
- return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
+ return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
}
get color() {
if (this.state.focused) {
diff --git a/dist/Views/Root.js b/dist/Views/Root.js
index 34e1304..a539ee5 100644
--- a/dist/Views/Root.js
+++ b/dist/Views/Root.js
@@ -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) => {
if (args) {
args.formValidate = args.action.scope.validateScope();
@@ -68,9 +77,9 @@ export class CardRootView extends React.PureComponent {
}
return args;
};
- this.populateCallbackParamData = (args) => {
- if (args && args.formValidate) {
- args.formData = FormContext.getInstance().getCallbackParamData(args.action.parameters);
+ this.populateSelectActionData = (args) => {
+ if (args) {
+ args.formData = Object.assign({}, (args.action.data || {}));
}
return args;
};
@@ -80,6 +89,7 @@ export class CardRootView extends React.PureComponent {
hostContext.registerOpenUrlHandler(this.onOpenUrl);
hostContext.registerSubmitHandler(this.onSubmit);
hostContext.registerCallbackHandler(this.onCallback);
+ hostContext.registerSelectActionHandler(this.onSelectAction);
hostContext.registerFocusHandler(this.props.onFocus);
hostContext.registerBlurHandler(this.props.onBlur);
hostContext.registerErrorHandler(this.props.onError);
@@ -102,9 +112,9 @@ export class CardRootView extends React.PureComponent {
actionType: ActionType.Submit
});
actionContext.registerHook({
- func: this.populateCallbackParamData,
- name: 'populateCallbackParamData',
- actionType: ActionType.Callback
+ func: this.populateSelectActionData,
+ name: 'populateSelectActionData',
+ actionType: ActionType.Select
});
}
render() {
diff --git a/examples/AdaptiveCards/Components/Basic/Touchable.js b/examples/AdaptiveCards/Components/Basic/Touchable.js
index e07d743..247c2db 100644
--- a/examples/AdaptiveCards/Components/Basic/Touchable.js
+++ b/examples/AdaptiveCards/Components/Basic/Touchable.js
@@ -13,28 +13,26 @@ import { Guid } from '../../Shared/Guid';
export class Touchable extends React.Component {
constructor(props) {
super(props);
+ this.testId = this.props.testId + Guid.newGuid();
}
componentDidMount() {
if (Platform.OS === 'android') {
- DeviceEventEmitter.addListener('KeyEnter' + this.props.testId, this.props.onPress);
+ DeviceEventEmitter.addListener('KeyEnter' + this.testId, this.props.onPress);
}
}
componentWillUnmount() {
if (Platform.OS === 'android') {
- DeviceEventEmitter.removeListener('KeyEnter' + this.props.testId, this.props.onPress);
+ DeviceEventEmitter.removeListener('KeyEnter' + this.testId, this.props.onPress);
}
}
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"]);
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))));
}
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();
- }
}
diff --git a/examples/AdaptiveCards/Components/Inputs/Contact.js b/examples/AdaptiveCards/Components/Inputs/Contact.js
new file mode 100644
index 0000000..2527762
--- /dev/null
+++ b/examples/AdaptiveCards/Components/Inputs/Contact.js
@@ -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))
+ ];
+ }
+}
diff --git a/examples/AdaptiveCards/Components/Inputs/InputBox.js b/examples/AdaptiveCards/Components/Inputs/InputBox.js
index c3bacc1..3c5aa10 100644
--- a/examples/AdaptiveCards/Components/Inputs/InputBox.js
+++ b/examples/AdaptiveCards/Components/Inputs/InputBox.js
@@ -87,7 +87,7 @@ export class InputBox extends React.Component {
return 1;
}
get height() {
- return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
+ return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
}
get color() {
if (this.state.focused) {
diff --git a/examples/AdaptiveCards/Components/Inputs/LabelInput.js b/examples/AdaptiveCards/Components/Inputs/LabelInput.js
new file mode 100644
index 0000000..69b0b49
--- /dev/null
+++ b/examples/AdaptiveCards/Components/Inputs/LabelInput.js
@@ -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);
+ }
+ }
+}
diff --git a/examples/AdaptiveCards/Contexts/CardContext.js b/examples/AdaptiveCards/Contexts/CardContext.js
new file mode 100644
index 0000000..dbec3aa
--- /dev/null
+++ b/examples/AdaptiveCards/Contexts/CardContext.js
@@ -0,0 +1,2 @@
+export class CardContext {
+}
diff --git a/examples/AdaptiveCards/Contexts/FormContext.js b/examples/AdaptiveCards/Contexts/FormContext.js
index 899af6f..abfd6bb 100644
--- a/examples/AdaptiveCards/Contexts/FormContext.js
+++ b/examples/AdaptiveCards/Contexts/FormContext.js
@@ -1,6 +1,7 @@
export class FormContext {
constructor() {
this.formFields = {};
+ this.fieldListeners = {};
}
static getInstance() {
if (FormContext.sharedInstance === undefined) {
@@ -14,6 +15,7 @@ export class FormContext {
value: value,
validate: validate
};
+ this.getFieldListeners(id).forEach((listener) => listener(value));
}
}
getField(id) {
@@ -54,17 +56,6 @@ export class FormContext {
}
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) {
let field = this.getField(id);
if (field) {
@@ -80,4 +71,19 @@ export class FormContext {
}
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;
+ }
}
diff --git a/examples/AdaptiveCards/Contexts/HostContext.js b/examples/AdaptiveCards/Contexts/HostContext.js
index 8d9e86d..197bcbf 100644
--- a/examples/AdaptiveCards/Contexts/HostContext.js
+++ b/examples/AdaptiveCards/Contexts/HostContext.js
@@ -38,6 +38,9 @@ export class HostContext {
registerCallbackHandler(handler) {
this.onCallback = handler;
}
+ registerSelectActionHandler(handler) {
+ this.onSelectAction = handler;
+ }
applyConfig(configJson) {
this.config.combine(new HostConfig(configJson));
}
@@ -59,6 +62,9 @@ export class HostContext {
case ActionType.Submit:
callback = this.onSubmit;
break;
+ case ActionType.Select:
+ callback = this.onSelectAction;
+ break;
case 'focus':
callback = this.onFocus;
break;
diff --git a/examples/AdaptiveCards/Contexts/MediaContext.js b/examples/AdaptiveCards/Contexts/MediaContext.js
deleted file mode 100644
index 8801ef1..0000000
--- a/examples/AdaptiveCards/Contexts/MediaContext.js
+++ /dev/null
@@ -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];
- }
-}
diff --git a/examples/AdaptiveCards/Schema/Abstract/ActionElement.js b/examples/AdaptiveCards/Schema/Abstract/ActionElement.js
index 855ef06..d6b3bf7 100644
--- a/examples/AdaptiveCards/Schema/Abstract/ActionElement.js
+++ b/examples/AdaptiveCards/Schema/Abstract/ActionElement.js
@@ -2,6 +2,7 @@ import { AbstractElement } from './AbstractElement';
export var ActionType;
(function (ActionType) {
ActionType["OpenUrl"] = "Action.OpenUrl";
+ ActionType["Select"] = "Action.Select";
ActionType["Submit"] = "Action.Submit";
ActionType["ShowCard"] = "Action.ShowCard";
ActionType["Callback"] = "Action.Callback";
diff --git a/examples/AdaptiveCards/Schema/Actions/SelectAction.js b/examples/AdaptiveCards/Schema/Actions/SelectAction.js
new file mode 100644
index 0000000..7259dfc
--- /dev/null
+++ b/examples/AdaptiveCards/Schema/Actions/SelectAction.js
@@ -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'];
+ }
+}
diff --git a/examples/AdaptiveCards/Schema/Factories/ActionFactory.js b/examples/AdaptiveCards/Schema/Factories/ActionFactory.js
index 9cc9ebd..2937d44 100644
--- a/examples/AdaptiveCards/Schema/Factories/ActionFactory.js
+++ b/examples/AdaptiveCards/Schema/Factories/ActionFactory.js
@@ -1,5 +1,6 @@
import { ActionType } from '../Abstract/ActionElement';
import { OpenUrlActionElement } from '../Actions/OpenUrlAction';
+import { SelectActionElement } from '../Actions/SelectAction';
import { ShowCardActionElement } from '../Actions/ShowCardAction';
import { SubmitActionElement } from '../Actions/SubmitAction';
export class ActionFactory {
@@ -18,6 +19,9 @@ export class ActionFactory {
case ActionType.ShowCard:
action = new ShowCardActionElement(json, parent);
break;
+ case ActionType.Select:
+ action = new SelectActionElement(json, parent);
+ break;
default:
action = undefined;
break;
diff --git a/examples/AdaptiveCards/Utils/ElementUtils.js b/examples/AdaptiveCards/Utils/ElementUtils.js
index 92a53d9..d0429f9 100644
--- a/examples/AdaptiveCards/Utils/ElementUtils.js
+++ b/examples/AdaptiveCards/Utils/ElementUtils.js
@@ -5,6 +5,10 @@ export class ElementUtils {
static isValue(type) {
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.valueTypes = ['Fact', 'Input.Choice'];
+ElementUtils.selectActionTargetTypes = ['Input.PeoplePicker'];
diff --git a/examples/AdaptiveCards/Views/Factories/ContentFactory.js b/examples/AdaptiveCards/Views/Factories/ContentFactory.js
index a63e152..2f8d9b8 100644
--- a/examples/AdaptiveCards/Views/Factories/ContentFactory.js
+++ b/examples/AdaptiveCards/Views/Factories/ContentFactory.js
@@ -11,6 +11,7 @@ import { FactSetView } from '../Containers/FactSet';
import { ImageSetView } from '../Containers/ImageSet';
import { DateInputView } from '../Inputs/DateInput';
import { NumberInputView } from '../Inputs/NumberInput';
+import { PeoplePickerView } from '../Inputs/PeoplePicker';
import { TextInputView } from '../Inputs/TextInput';
import { TimeInputView } from '../Inputs/TimeInput';
export class ContentFactory {
@@ -61,6 +62,8 @@ export class ContentFactory {
return (React.createElement(DateInputView, { key: 'DateInputView' + index, element: element, index: index, theme: theme }));
case ContentElementType.TimeInput:
return (React.createElement(TimeInputView, { key: 'TimeInputView' + index, element: element, index: index, theme: theme }));
+ case ContentElementType.PeoplePicker:
+ return (React.createElement(PeoplePickerView, { key: 'PeoplePickerView' + index, element: element, index: index, theme: theme }));
default:
return null;
}
diff --git a/examples/AdaptiveCards/Views/Inputs/DateInput.js b/examples/AdaptiveCards/Views/Inputs/DateInput.js
index 9346dd5..ec8a3db 100644
--- a/examples/AdaptiveCards/Views/Inputs/DateInput.js
+++ b/examples/AdaptiveCards/Views/Inputs/DateInput.js
@@ -75,7 +75,7 @@ export class DateInputView extends React.Component {
return 1;
}
get height() {
- return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
+ return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
}
get color() {
if (this.state.focused) {
diff --git a/examples/AdaptiveCards/Views/Inputs/PeoplePicker.js b/examples/AdaptiveCards/Views/Inputs/PeoplePicker.js
new file mode 100644
index 0000000..1c710d7
--- /dev/null
+++ b/examples/AdaptiveCards/Views/Inputs/PeoplePicker.js
@@ -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
+ };
+ });
+ }
+}
diff --git a/examples/AdaptiveCards/Views/Inputs/TimeInput.js b/examples/AdaptiveCards/Views/Inputs/TimeInput.js
index fb47946..28be876 100644
--- a/examples/AdaptiveCards/Views/Inputs/TimeInput.js
+++ b/examples/AdaptiveCards/Views/Inputs/TimeInput.js
@@ -75,7 +75,7 @@ export class TimeInputView extends React.Component {
return 1;
}
get height() {
- return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
+ return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
}
get color() {
if (this.state.focused) {
diff --git a/examples/AdaptiveCards/Views/Root.js b/examples/AdaptiveCards/Views/Root.js
index 34e1304..a539ee5 100644
--- a/examples/AdaptiveCards/Views/Root.js
+++ b/examples/AdaptiveCards/Views/Root.js
@@ -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) => {
if (args) {
args.formValidate = args.action.scope.validateScope();
@@ -68,9 +77,9 @@ export class CardRootView extends React.PureComponent {
}
return args;
};
- this.populateCallbackParamData = (args) => {
- if (args && args.formValidate) {
- args.formData = FormContext.getInstance().getCallbackParamData(args.action.parameters);
+ this.populateSelectActionData = (args) => {
+ if (args) {
+ args.formData = Object.assign({}, (args.action.data || {}));
}
return args;
};
@@ -80,6 +89,7 @@ export class CardRootView extends React.PureComponent {
hostContext.registerOpenUrlHandler(this.onOpenUrl);
hostContext.registerSubmitHandler(this.onSubmit);
hostContext.registerCallbackHandler(this.onCallback);
+ hostContext.registerSelectActionHandler(this.onSelectAction);
hostContext.registerFocusHandler(this.props.onFocus);
hostContext.registerBlurHandler(this.props.onBlur);
hostContext.registerErrorHandler(this.props.onError);
@@ -102,9 +112,9 @@ export class CardRootView extends React.PureComponent {
actionType: ActionType.Submit
});
actionContext.registerHook({
- func: this.populateCallbackParamData,
- name: 'populateCallbackParamData',
- actionType: ActionType.Callback
+ func: this.populateSelectActionData,
+ name: 'populateSelectActionData',
+ actionType: ActionType.Select
});
}
render() {
diff --git a/examples/App.js b/examples/App.js
index 3272a79..67a30e0 100644
--- a/examples/App.js
+++ b/examples/App.js
@@ -31,67 +31,7 @@ export default class App extends React.Component {
padding: 10,
backgroundColor: 'white',
}}>
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
- {this.renderGap()}
-
+
);
}
@@ -99,4 +39,8 @@ export default class App extends React.Component {
renderGap() {
return ;
};
+
+ onCallback = (url, data) => {
+ return Promise.resolve(mockData.peopleSuggestion);
+ }
}
diff --git a/examples/mockData/index.js b/examples/mockData/index.js
index 7705fd2..c6e57e1 100644
--- a/examples/mockData/index.js
+++ b/examples/mockData/index.js
@@ -19,7 +19,8 @@ var searchEmail = require('./skills/search_email.json');
var emailSent = require('./skills/email_sent.json');
var sendText = require('./skills/send_text.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 fact = require('./fact.json');
var vocabulary = require('./vocabulary.json');
@@ -52,4 +53,5 @@ exports["default"] = {
vocabulary: vocabulary,
dinning: dinning,
bingMap: bingMap,
+ peopleSuggestion: peopleSuggestion,
};
diff --git a/examples/mockData/peopleSuggestion.json b/examples/mockData/peopleSuggestion.json
new file mode 100644
index 0000000..c3449eb
--- /dev/null
+++ b/examples/mockData/peopleSuggestion.json
@@ -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"
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/package.json b/package.json
index 0837332..e2f44df 100644
--- a/package.json
+++ b/package.json
@@ -23,20 +23,20 @@
"silent": true
},
"devDependencies": {
- "@types/lodash": "^4.14.116",
- "@types/react": "^16.4.8",
- "@types/react-native": "^0.56.6",
- "del": "^3.0.0",
- "gulp": "^3.9.1",
- "gulp-imagemin": "^4.1.0",
- "gulp-tslint": "^8.1.3",
- "gulp-typescript": "^5.0.0-alpha.3",
+ "@types/lodash": "latest",
+ "@types/react": "latest",
+ "@types/react-native": "latest",
+ "del": "latest",
+ "gulp": "latest",
+ "gulp-imagemin": "latest",
+ "gulp-tslint": "latest",
+ "gulp-typescript": "latest",
"react-devtools": "latest",
- "run-sequence": "^2.2.1",
- "tslint": "^5.11.0",
- "typescript": "^3.0.1"
+ "run-sequence": "latest",
+ "tslint": "latest",
+ "typescript": "latest"
},
"dependencies": {
- "lodash": "^4.17.10"
+ "lodash": "latest"
}
}
diff --git a/src/Components/Basic/Touchable.tsx b/src/Components/Basic/Touchable.tsx
index 50e5b20..2a961d1 100644
--- a/src/Components/Basic/Touchable.tsx
+++ b/src/Components/Basic/Touchable.tsx
@@ -25,19 +25,23 @@ interface IProps {
}
export class Touchable extends React.Component {
+ private testId: string;
+
constructor(props: IProps) {
super(props);
+
+ this.testId = this.props.testId + Guid.newGuid();
}
public componentDidMount() {
if (Platform.OS === 'android') {
- DeviceEventEmitter.addListener('KeyEnter' + this.props.testId, this.props.onPress);
+ DeviceEventEmitter.addListener('KeyEnter' + this.testId, this.props.onPress);
}
}
public componentWillUnmount() {
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 {
onPress={onPress}
onLongPress={onLongPress}
accessible={true}
- testID={this.uniqueTestId}
+ testID={this.testId}
useForeground={true}
hitSlop={hitSlop}
background={TouchableNativeFeedback.SelectableBackground()}
@@ -81,7 +85,7 @@ export class Touchable extends React.Component {
onPress={onPress}
onLongPress={onLongPress}
accessible={true}
- testID={this.uniqueTestId}
+ testID={this.testId}
activeOpacity={activeOpacity}
style={style}
hitSlop={hitSlop}
@@ -93,8 +97,4 @@ export class Touchable extends React.Component {
);
}
}
-
- private get uniqueTestId() {
- return this.props.testId + Guid.newGuid();
- }
}
diff --git a/src/Components/Inputs/Contact.tsx b/src/Components/Inputs/Contact.tsx
new file mode 100644
index 0000000..7493993
--- /dev/null
+++ b/src/Components/Inputs/Contact.tsx
@@ -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 {
+ public render() {
+ if (this.props.onSelect) {
+ return this.renderTouchableBlock();
+ } else {
+ return this.renderNonTouchableBlock();
+ }
+ }
+
+ private renderTouchableBlock() {
+ return (
+
+ {this.renderContent()}
+
+ );
+ }
+
+ private renderNonTouchableBlock() {
+ return (
+
+ {this.renderContent()}
+
+ );
+ }
+
+ private renderContent() {
+ return [
+ ,
+
+
+ {this.props.mainInfo}
+
+
+ {this.props.subInfo}
+
+
+ ];
+ }
+
+ private onPress = () => {
+ if (this.props.onSelect) {
+ this.props.onSelect(this.props.hiddenFields);
+ }
+ }
+}
diff --git a/src/Components/Inputs/InputBox.tsx b/src/Components/Inputs/InputBox.tsx
index 2b53331..3343289 100644
--- a/src/Components/Inputs/InputBox.tsx
+++ b/src/Components/Inputs/InputBox.tsx
@@ -147,7 +147,7 @@ export class InputBox extends React.Component {
}
private get height() {
- return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
+ return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
}
private get color() {
diff --git a/src/Components/Inputs/LabelInput.tsx b/src/Components/Inputs/LabelInput.tsx
new file mode 100644
index 0000000..2122987
--- /dev/null
+++ b/src/Components/Inputs/LabelInput.tsx
@@ -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;
+ onRequestSuggestion?: (input: string) => void;
+ onFocus?: () => void;
+ onBlur?: () => void;
+ validateInput?: (input: string) => boolean;
+}
+
+interface IState {
+ focused: boolean;
+}
+
+export class LabelInput extends React.Component {
+ 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 (
+
+ {this.renderInputArea()}
+ {this.renderSuggestions()}
+
+ );
+ }
+
+ private renderInputArea() {
+ return (
+
+ {this.renderLabels()}
+ {this.renderInputBox()}
+
+ );
+ }
+
+ private renderLabels() {
+ if (this.props.labels) {
+ return this.props.labels.map((label, index) => {
+ return (
+
+ {label.title}
+
+ );
+ });
+ }
+ return undefined;
+ }
+
+ private renderInputBox() {
+ return (
+ 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 [
+ ,
+
+ {this.props.suggestionView}
+
+ ];
+ }
+ 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);
+ }
+ }
+}
diff --git a/src/Contexts/CardContext.ts b/src/Contexts/CardContext.ts
new file mode 100644
index 0000000..f56648c
--- /dev/null
+++ b/src/Contexts/CardContext.ts
@@ -0,0 +1,3 @@
+export class CardContext {
+
+}
diff --git a/src/Contexts/FormContext.ts b/src/Contexts/FormContext.ts
index 823fbe8..2944f43 100644
--- a/src/Contexts/FormContext.ts
+++ b/src/Contexts/FormContext.ts
@@ -5,6 +5,7 @@ export interface FormField {
export class FormContext {
private formFields: { [id: string]: FormField } = {};
+ private fieldListeners: { [id: string]: Array<(value: string) => void> } = {};
private static sharedInstance: FormContext;
@@ -23,6 +24,7 @@ export class FormContext {
value: value,
validate: validate
};
+ this.getFieldListeners(id).forEach((listener) => listener(value));
}
}
@@ -67,18 +69,6 @@ export class FormContext {
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 {
let field = this.getField(id);
if (field) {
@@ -95,4 +85,20 @@ export class FormContext {
}
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;
+ }
}
diff --git a/src/Contexts/HostContext.ts b/src/Contexts/HostContext.ts
index 54e230f..65ce2ae 100644
--- a/src/Contexts/HostContext.ts
+++ b/src/Contexts/HostContext.ts
@@ -2,6 +2,7 @@ import { ConfigManager } from '../Config/ConfigManager';
import { HostConfig } from '../Config/Types';
import { ActionType } from '../Schema/Abstract/ActionElement';
import { OpenUrlActionElement } from '../Schema/Actions/OpenUrlAction';
+import { SelectActionElement } from '../Schema/Actions/SelectAction';
import { ShowCardActionElement } from '../Schema/Actions/ShowCardAction';
import { SubmitActionElement } from '../Schema/Actions/SubmitAction';
import { IElement } from '../Schema/Interfaces/IElement';
@@ -19,6 +20,7 @@ export class HostContext {
private onShowCard: (args?: ActionEventHandlerArgs) => void;
private onSubmit: (args?: ActionEventHandlerArgs) => void;
private onCallback: (args?: ActionEventHandlerArgs) => void;
+ private onSelectAction: (args?: ActionEventHandlerArgs) => void;
private static sharedInstance: HostContext;
@@ -69,6 +71,10 @@ export class HostContext {
this.onCallback = handler;
}
+ public registerSelectActionHandler(handler: (args?: ActionEventHandlerArgs) => void) {
+ this.onSelectAction = handler;
+ }
+
public applyConfig(configJson: any) {
this.config.combine(new HostConfig(configJson));
}
@@ -92,6 +98,9 @@ export class HostContext {
case ActionType.Submit:
callback = this.onSubmit;
break;
+ case ActionType.Select:
+ callback = this.onSelectAction;
+ break;
case 'focus':
callback = this.onFocus;
break;
diff --git a/src/Contexts/MediaContext.ts b/src/Contexts/MediaContext.ts
deleted file mode 100644
index c3a97ae..0000000
--- a/src/Contexts/MediaContext.ts
+++ /dev/null
@@ -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];
- }
-}
diff --git a/src/Schema/Abstract/ActionElement.ts b/src/Schema/Abstract/ActionElement.ts
index 0afad24..fa8da93 100644
--- a/src/Schema/Abstract/ActionElement.ts
+++ b/src/Schema/Abstract/ActionElement.ts
@@ -5,6 +5,7 @@ import { AbstractElement } from './AbstractElement';
export enum ActionType {
OpenUrl = 'Action.OpenUrl',
+ Select = 'Action.Select',
Submit = 'Action.Submit',
ShowCard = 'Action.ShowCard',
Callback = 'Action.Callback',
diff --git a/src/Schema/Actions/SelectAction.ts b/src/Schema/Actions/SelectAction.ts
new file mode 100644
index 0000000..8b19f51
--- /dev/null
+++ b/src/Schema/Actions/SelectAction.ts
@@ -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'];
+ }
+}
diff --git a/src/Schema/Cards/Card.ts b/src/Schema/Cards/Card.ts
index b3836c4..1d93241 100644
--- a/src/Schema/Cards/Card.ts
+++ b/src/Schema/Cards/Card.ts
@@ -15,6 +15,7 @@ export class CardElement extends ScopeElement {
public readonly actions?: IAction[];
public readonly body?: IContent[];
public readonly backgroundImage?: string;
+ public readonly cardSource?: string;
constructor(json: any, parent: IElement) {
super(json, parent);
diff --git a/src/Schema/Containers/ColumnSet.ts b/src/Schema/Containers/ColumnSet.ts
index a192f04..b277635 100644
--- a/src/Schema/Containers/ColumnSet.ts
+++ b/src/Schema/Containers/ColumnSet.ts
@@ -11,6 +11,7 @@ export class ColumnSetElement extends ScopeElement {
if (this.isValid) {
this.columns = [];
+
if (json.columns) {
json.columns.forEach((item: any) => {
let column: ColumnElement = new ColumnElement(item, this);
diff --git a/src/Schema/Factories/ActionFactory.ts b/src/Schema/Factories/ActionFactory.ts
index 1d9c64e..22e2480 100644
--- a/src/Schema/Factories/ActionFactory.ts
+++ b/src/Schema/Factories/ActionFactory.ts
@@ -1,5 +1,6 @@
import { ActionElement, ActionType } from '../Abstract/ActionElement';
import { OpenUrlActionElement } from '../Actions/OpenUrlAction';
+import { SelectActionElement } from '../Actions/SelectAction';
import { ShowCardActionElement } from '../Actions/ShowCardAction';
import { SubmitActionElement } from '../Actions/SubmitAction';
import { IAction } from '../Interfaces/IAction';
@@ -23,6 +24,9 @@ export class ActionFactory {
case ActionType.ShowCard:
action = new ShowCardActionElement(json, parent);
break;
+ case ActionType.Select:
+ action = new SelectActionElement(json, parent);
+ break;
default:
action = undefined;
break;
diff --git a/src/Schema/Interfaces/IScope.ts b/src/Schema/Interfaces/IScope.ts
index 3884b5e..55cedbe 100644
--- a/src/Schema/Interfaces/IScope.ts
+++ b/src/Schema/Interfaces/IScope.ts
@@ -2,7 +2,7 @@ import { IAction } from './IAction';
import { IContent } from './IContent';
export interface IScope extends IContent {
- readonly selectAction?: IContent;
+ readonly selectAction?: IAction;
readonly backgroundImage?: string | { url: string };
readonly action: IAction;
readonly inputFields: string[];
diff --git a/src/Utils/ElementUtils.ts b/src/Utils/ElementUtils.ts
index e68c2ec..0f4fe2e 100644
--- a/src/Utils/ElementUtils.ts
+++ b/src/Utils/ElementUtils.ts
@@ -1,6 +1,7 @@
export class ElementUtils {
private static inputTypes = ['Input.Text', 'Input.Number', 'Input.Date', 'Input.Time', 'Input.Toggle', 'Input.ChoiceSet'];
private static valueTypes = ['Fact', 'Input.Choice'];
+ private static selectActionTargetTypes = ['Input.PeoplePicker'];
public static isInput(type: string) {
return ElementUtils.inputTypes.indexOf(type) >= 0;
@@ -9,4 +10,8 @@ export class ElementUtils {
public static isValue(type: string) {
return ElementUtils.valueTypes.indexOf(type) >= 0;
}
+
+ public static isSelectActionTarget(type: string) {
+ return ElementUtils.selectActionTargetTypes.indexOf(type) >= 0;
+ }
}
diff --git a/src/Views/Factories/ContentFactory.tsx b/src/Views/Factories/ContentFactory.tsx
index 8b9ebc1..faa5e14 100644
--- a/src/Views/Factories/ContentFactory.tsx
+++ b/src/Views/Factories/ContentFactory.tsx
@@ -11,6 +11,7 @@ import { FactSetElement } from '../../Schema/Containers/FactSet';
import { ImageSetElement } from '../../Schema/Containers/ImageSet';
import { DateInputElement } from '../../Schema/Inputs/DateInput';
import { NumberInputElement } from '../../Schema/Inputs/NumberInput';
+import { PeoplePickerElement } from '../../Schema/Inputs/PeoplePicker';
import { TextInputElement } from '../../Schema/Inputs/TextInput';
import { TimeInputElement } from '../../Schema/Inputs/TimeInput';
import { ImageView } from '../CardElements/Image';
@@ -22,6 +23,7 @@ import { FactSetView } from '../Containers/FactSet';
import { ImageSetView } from '../Containers/ImageSet';
import { DateInputView } from '../Inputs/DateInput';
import { NumberInputView } from '../Inputs/NumberInput';
+import { PeoplePickerView } from '../Inputs/PeoplePicker';
import { TextInputView } from '../Inputs/TextInput';
import { TimeInputView } from '../Inputs/TimeInput';
@@ -160,6 +162,15 @@ export class ContentFactory {
theme={theme}
/>
);
+ case ContentElementType.PeoplePicker:
+ return (
+
+ );
default:
return null;
}
diff --git a/src/Views/Inputs/DateInput.tsx b/src/Views/Inputs/DateInput.tsx
index 6849060..0e0f23f 100644
--- a/src/Views/Inputs/DateInput.tsx
+++ b/src/Views/Inputs/DateInput.tsx
@@ -137,7 +137,7 @@ export class DateInputView extends React.Component {
}
private get height() {
- return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
+ return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
}
private get color() {
diff --git a/src/Views/Inputs/PeoplePicker.tsx b/src/Views/Inputs/PeoplePicker.tsx
new file mode 100644
index 0000000..e062d70
--- /dev/null
+++ b/src/Views/Inputs/PeoplePicker.tsx
@@ -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 {
+ 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 (
+
+ );
+ }
+
+ 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) => {
+ 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
+ };
+ });
+ }
+}
diff --git a/src/Views/Inputs/TimeInput.tsx b/src/Views/Inputs/TimeInput.tsx
index ae1fda7..2f6a8ab 100644
--- a/src/Views/Inputs/TimeInput.tsx
+++ b/src/Views/Inputs/TimeInput.tsx
@@ -137,7 +137,7 @@ export class TimeInputView extends React.Component {
}
private get height() {
- return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
+ return this.fontSize * this.numberOfLine + this.paddingVertical * 2 + 2;
}
private get color() {
diff --git a/src/Views/Root.tsx b/src/Views/Root.tsx
index 58a483d..1a524fc 100644
--- a/src/Views/Root.tsx
+++ b/src/Views/Root.tsx
@@ -9,6 +9,7 @@ import { FormContext } from '../Contexts/FormContext';
import { HostContext } from '../Contexts/HostContext';
import { ActionType } from '../Schema/Abstract/ActionElement';
import { OpenUrlActionElement } from '../Schema/Actions/OpenUrlAction';
+import { SelectActionElement } from '../Schema/Actions/SelectAction';
import { SubmitActionElement } from '../Schema/Actions/SubmitAction';
import { CardElement } from '../Schema/Cards/Card';
import { CallbackAction } from '../Schema/Internal/CallbackAction';
@@ -47,6 +48,7 @@ export class CardRootView extends React.PureComponent {
hostContext.registerOpenUrlHandler(this.onOpenUrl);
hostContext.registerSubmitHandler(this.onSubmit);
hostContext.registerCallbackHandler(this.onCallback);
+ hostContext.registerSelectActionHandler(this.onSelectAction);
hostContext.registerFocusHandler(this.props.onFocus);
hostContext.registerBlurHandler(this.props.onBlur);
@@ -76,9 +78,9 @@ export class CardRootView extends React.PureComponent {
});
actionContext.registerHook({
- func: this.populateCallbackParamData,
- name: 'populateCallbackParamData',
- actionType: ActionType.Callback
+ func: this.populateSelectActionData,
+ name: 'populateSelectActionData',
+ actionType: ActionType.Select
});
}
@@ -140,6 +142,16 @@ export class CardRootView extends React.PureComponent {
}
}
+ private onSelectAction = (args: ActionEventHandlerArgs) => {
+ if (args) {
+ let currentValue = JSON.parse(FormContext.getInstance().getFieldValue(args.action.targetFormField)) as Array;
+ if (currentValue) {
+ currentValue.push(args.formData);
+ FormContext.getInstance().updateField(args.action.targetFormField, JSON.stringify(currentValue), true);
+ }
+ }
+ }
+
private validateForm = (args: ActionEventHandlerArgs) => {
if (args) {
args.formValidate = args.action.scope.validateScope();
@@ -164,9 +176,11 @@ export class CardRootView extends React.PureComponent {
return args;
}
- private populateCallbackParamData = (args: ActionEventHandlerArgs) => {
- if (args && args.formValidate) {
- args.formData = FormContext.getInstance().getCallbackParamData(args.action.parameters);
+ private populateSelectActionData = (args: ActionEventHandlerArgs) => {
+ if (args) {
+ args.formData = {
+ ...(args.action.data || {}),
+ };
}
return args;
}
diff --git a/yarn.lock b/yarn.lock
index c863bf0..f5c28c7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6,7 +6,7 @@
version "1.3.0"
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"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.116.tgz#5ccf215653e3e8c786a58390751033a9adca0eb9"
@@ -20,19 +20,26 @@
dependencies:
"@types/react" "*"
-"@types/react-native@^0.56.6":
- version "0.56.6"
- resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.56.6.tgz#d9c60e1df95ec0402fa956225558c8a48f37f23f"
+"@types/react-native@latest":
+ version "0.56.9"
+ resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.56.9.tgz#87e5877b98aed2b1dcd1def6f69927b24c2bd7e5"
dependencies:
"@types/react" "*"
-"@types/react@*", "@types/react@^16.4.8":
+"@types/react@*":
version "16.4.8"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.8.tgz#ff0440429783df0927bdcd430fa1225f7c08cf36"
dependencies:
"@types/prop-types" "*"
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:
version "5.5.2"
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"
isobject "^3.0.1"
-del@^3.0.0:
+del@latest:
version "3.0.0"
resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
dependencies:
@@ -1598,7 +1605,7 @@ gulp-decompress@^1.2.0:
gulp-util "^3.0.1"
readable-stream "^2.0.2"
-gulp-imagemin@^4.1.0:
+gulp-imagemin@latest:
version "4.1.0"
resolved "https://registry.yarnpkg.com/gulp-imagemin/-/gulp-imagemin-4.1.0.tgz#5ce347f1d1706fed3cc8f1777ca9094a583b50b7"
dependencies:
@@ -1629,7 +1636,7 @@ gulp-sourcemaps@1.6.0:
through2 "^2.0.0"
vinyl "^1.0.0"
-gulp-tslint@^8.1.3:
+gulp-tslint@latest:
version "8.1.3"
resolved "https://registry.yarnpkg.com/gulp-tslint/-/gulp-tslint-8.1.3.tgz#a89ed144038ae861ee7bfea9528272d126a93da1"
dependencies:
@@ -1640,7 +1647,7 @@ gulp-tslint@^8.1.3:
plugin-error "1.0.1"
through "~2.3.8"
-gulp-typescript@^5.0.0-alpha.3:
+gulp-typescript@latest:
version "5.0.0-alpha.3"
resolved "https://registry.yarnpkg.com/gulp-typescript/-/gulp-typescript-5.0.0-alpha.3.tgz#c49a306cbbb8c97f5fe8a79208671b6642ef9861"
dependencies:
@@ -1674,7 +1681,7 @@ gulp-util@^3.0.0, gulp-util@^3.0.1:
through2 "^2.0.0"
vinyl "^0.5.0"
-gulp@^3.9.1:
+gulp@latest:
version "3.9.1"
resolved "https://registry.yarnpkg.com/gulp/-/gulp-3.9.1.tgz#571ce45928dd40af6514fc4011866016c13845b4"
dependencies:
@@ -2437,7 +2444,7 @@ lodash.templatesettings@^3.0.0:
lodash._reinterpolate "^3.0.0"
lodash.escape "^3.0.0"
-lodash@^4.17.10:
+lodash@latest:
version "4.17.10"
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:
glob "^7.0.5"
-run-sequence@^2.2.1:
+run-sequence@latest:
version "2.2.1"
resolved "https://registry.yarnpkg.com/run-sequence/-/run-sequence-2.2.1.tgz#1ce643da36fd8c7ea7e1a9329da33fc2b8898495"
dependencies:
@@ -3857,7 +3864,7 @@ tslib@^1.8.0, tslib@^1.8.1:
version "1.9.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
-tslint@^5.11.0:
+tslint@latest:
version "5.11.0"
resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed"
dependencies:
@@ -3898,7 +3905,7 @@ typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
-typescript@^3.0.1:
+typescript@latest:
version "3.0.1"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.1.tgz#43738f29585d3a87575520a4b93ab6026ef11fdb"