Merged PR 751703: Modify gulpfile, add web tool and fix cannot set children issue

Modify gulpfile to add AdaptiveCard  to web tool
Add web tool of AdaptiveCard
Fix cannot set children issue because the property children only have a getter
This commit is contained in:
Haobin Guo 2018-08-31 09:15:30 +00:00
Родитель d049cb45c6
Коммит 70c1851b4d
129 изменённых файлов: 17259 добавлений и 10 удалений

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

@ -1,6 +1,7 @@
var del = require('del');
var gulp = require('gulp');
var imagemin = require('gulp-imagemin');
var rename = require('gulp-rename');
var gulpTslint = require('gulp-tslint');
var runSequence = require('run-sequence');
var typescript = require('gulp-typescript');
@ -12,11 +13,12 @@ var path = {
src: './src/',
dist: './dist/',
example: './examples/AdaptiveCards/',
tool: './tool/src/assets/AdaptiveCards/',
};
// Clean destination folder
gulp.task('clean', function () {
return del([path.dist, path.example]);
return del([path.dist, path.example, path.tool]);
});
// Minify images in place
@ -24,7 +26,12 @@ gulp.task('minify-img', function () {
return gulp.src(path.src + 'Assets/**/*.png')
.pipe(imagemin())
.pipe(gulp.dest(path.dist + 'Assets'))
.pipe(gulp.dest(path.example + 'Assets'));
.pipe(gulp.dest(path.example + 'Assets'))
.pipe(rename(function(opt) {
opt.basename = opt.basename.replace(/@[^.]*/, '');
return opt;
}))
.pipe(gulp.dest(path.tool + 'Assets'));
});
// Checks your TypeScript code for readability, maintainability, and functionality errors.
@ -44,7 +51,8 @@ gulp.task('lint-ts', function () {
gulp.task('copy-json', function () {
gulp.src(path.src + '**/*.json')
.pipe(gulp.dest(path.dist))
.pipe(gulp.dest(path.example));
.pipe(gulp.dest(path.example))
.pipe(gulp.dest(path.tool));
});
gulp.task('compile-ts', function () {
@ -52,7 +60,9 @@ gulp.task('compile-ts', function () {
.pipe(tsProject());
return tsResult.js
.pipe(gulp.dest(path.dist))
.pipe(gulp.dest(path.example));
.pipe(gulp.dest(path.example))
.pipe(gulp.dest(path.tool));
});
gulp.task('default', function (cb) {

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

@ -31,6 +31,7 @@
"del": "latest",
"gulp": "latest",
"gulp-imagemin": "latest",
"gulp-rename": "^1.4.0",
"gulp-tslint": "latest",
"gulp-typescript": "latest",
"react-devtools": "latest",

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

@ -1,6 +1,6 @@
import * as React from 'react';
import {
AccessibilityTrait,
AccessibilityTraits,
DeviceEventEmitter,
LayoutChangeEvent,
Platform,
@ -15,7 +15,7 @@ interface IProps {
disabled?: boolean;
style?: object;
accessibilityLabel?: string;
accessibilityTraits?: AccessibilityTrait | AccessibilityTrait[];
accessibilityTraits?: AccessibilityTraits | AccessibilityTraits[];
accessibilityComponentType?: 'none' | 'button' | 'radiobutton_checked' | 'radiobutton_unchecked';
hitSlop?: object;
activeOpacity?: number;

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

@ -39,8 +39,10 @@ export class Contact extends React.Component<IProps> {
private renderNonTouchableBlock() {
return (
<View
alignSelf='stretch'
flexDirection='row'
style={{
alignSelf: 'stretch',
flexDirection: 'row'
}}
>
{this.renderContent()}
</View>

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

@ -4,7 +4,6 @@ import { TreeNode } from '../../Shared/Types';
export abstract class AbstractModel extends TreeNode<AbstractModel> {
public readonly type: string;
public context: CardContext;
public children: AbstractModel[] = [];
constructor(json: any, parent: AbstractModel, context: CardContext) {
super(parent);
@ -15,4 +14,8 @@ export abstract class AbstractModel extends TreeNode<AbstractModel> {
this.context.fit = 'content';
}
}
public get children(): AbstractModel[] {
return [];
}
}

11
tool/.babelrc Normal file
Просмотреть файл

@ -0,0 +1,11 @@
{
"presets": [
"env",
"react"
],
"plugins": [
"transform-runtime",
"transform-async-to-generator",
"transform-object-rest-spread"
]
}

7
tool/.eslintignore Normal file
Просмотреть файл

@ -0,0 +1,7 @@
# js vendor file with import/require
src/assets/**
# dependencies
node_modules/
dist

9
tool/.eslintrc Normal file
Просмотреть файл

@ -0,0 +1,9 @@
{
"parser": "babel-eslint",
"plugins": [
"react"
],
"rules": {
},
"extends": ["eslint:recommended", "plugin:react/recommended"]
}

57
tool/package.json Normal file
Просмотреть файл

@ -0,0 +1,57 @@
{
"name": "adaptive-cards-web",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "./node_modules/.bin/webpack-dev-server --mode development --inline --open --hot",
"build": "webpack-dev-server --mode production",
"lint": "eslint --ext .js --ignore-path .eslintignore src "
},
"keywords": [],
"author": "Haobin Guo",
"license": "ISC",
"dependencies": {
"antd": "^3.8.2",
"prop-types": "^15.6.2",
"react": "^16.4.2",
"react-art": "^16.4.2",
"react-custom-scrollbars": "^4.2.1",
"react-dom": "^16.4.2",
"react-monaco-editor": "^0.18.0",
"react-native": "^0.56.0",
"react-native-web": "0.6.0",
"react-redux": "^5.0.7",
"react-router": "^4.3.1",
"react-scripts": "^1.1.4",
"react-scroll": "^1.7.10",
"react-scrollbar": "^0.5.4",
"redux": "^4.0.0"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-eslint": "^9.0.0",
"babel-loader": "^7.1.5",
"babel-plugin-transform-async-to-generator": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"css-loader": "^1.0.0",
"eslint": "^5.4.0",
"eslint-loader": "^2.1.0",
"eslint-plugin-react": "^7.11.1",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^3.2.0",
"monaco-editor-webpack-plugin": "^1.5.1",
"react-svg-loader": "^2.1.0",
"style-loader": "^0.22.1",
"ts-loader": "^4.5.0",
"url-loader": "^1.1.1",
"webpack": "^4.16.5",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.5"
}
}

11
tool/src/Constants.js Normal file
Просмотреть файл

@ -0,0 +1,11 @@
export const MODIFY_CONFIG = "MODIFY_CONFIG";
export const MODIFY_CARD = "MODIFY_CARD";
export const SET_MODE = "SET_MODE";
export const ADD_CARD = "ADD_CARD";
export const REMOVE_CARD = "REMOVE_CARD";
export const SET_CARD_SELECTED = "SET_CARD_SELECTED";

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

@ -0,0 +1,45 @@
import * as Constants from '../Constants'
export function setMode(mode) {
return {
type: Constants.SET_MODE,
mode
}
}
export function modifyConfig(config) {
return {
type: Constants.MODIFY_CONFIG,
config
}
}
export function modifyCard(id, card) {
return {
type: Constants.MODIFY_CARD,
id,
card
}
}
export function setCardSelected(id, selected) {
return {
type: Constants.SET_CARD_SELECTED,
id,
selected
}
}
export function addCard(name) {
return {
type: Constants.MODIFY_CARD,
name
}
}
export function removeCard(id) {
return {
type: Constants.REMOVE_CARD,
id
}
}

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 13 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.3 KiB

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

@ -0,0 +1,39 @@
import * as React from 'react';
import { Text, View } from 'react-native';
import { StyleManager } from '../../Styles/StyleManager';
export class Banner extends React.Component {
render() {
return (React.createElement(View, { style: {
backgroundColor: this.backgroundColor,
paddingTop: 8,
paddingRight: 8,
paddingBottom: 8,
paddingLeft: 8,
marginTop: 4,
marginRight: 4,
marginBottom: 4,
marginLeft: 4
} },
React.createElement(Text, { style: {
color: this.color,
} }, this.props.title),
this.props.children));
}
get backgroundColor() {
switch (this.props.level) {
case 'info':
return StyleManager.getColor('accent', this.props.theme, false);
case 'warning':
return StyleManager.getColor('warning', this.props.theme, false);
case 'error':
return StyleManager.getColor('attention', this.props.theme, false);
case 'success':
return StyleManager.getColor('good', this.props.theme, false);
default:
return StyleManager.getColor('accent', this.props.theme, false);
}
}
get color() {
return StyleManager.getBackgroundColor(this.props.theme);
}
}

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

@ -0,0 +1,23 @@
import React from 'react';
import { Image, StyleSheet, View, } from 'react-native';
export class ImageBackground extends React.PureComponent {
render() {
return (React.createElement(View, { style: [{
flex: this.props.flex,
position: 'relative',
marginTop: this.props.marginTop,
marginRight: this.props.marginRight,
marginBottom: this.props.marginBottom,
marginLeft: this.props.marginLeft,
paddingTop: this.props.paddingTop,
paddingRight: this.props.paddingRight,
paddingBottom: this.props.paddingBottom,
paddingLeft: this.props.paddingLeft
}, this.props.containerStyle] },
React.createElement(Image, { source: { uri: this.props.url }, style: [
StyleSheet.absoluteFill,
this.props.imageStyle
] }),
this.props.children));
}
}

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

@ -0,0 +1,114 @@
import * as React from 'react';
import { Image, View } from 'react-native';
import { UrlUtils } from '../../Utils/UrlUtils';
import { Svg } from './Svg';
import { Touchable } from './Touchable';
export class ImageBlock extends React.Component {
constructor(props) {
super(props);
this.onError = (err) => {
console.log(err);
this.setState({
loaded: false
});
};
this.state = {
loaded: true,
};
}
render() {
if (this.props.onPress) {
return this.renderTouchableBlock();
}
else {
return this.renderNonTouchableBlock();
}
}
renderTouchableBlock() {
return (React.createElement(Touchable, { onPress: this.props.onPress, onLayout: this.props.onLayout, style: {
flex: this.props.flex,
alignContent: 'center',
alignItems: 'center',
alignSelf: this.props.alignSelf,
marginTop: this.props.marginTop,
marginRight: this.props.marginRight,
marginBottom: this.props.marginBottom,
marginLeft: this.props.marginLeft,
paddingTop: this.props.paddingTop,
paddingRight: this.props.paddingRight,
paddingBottom: this.props.paddingBottom,
paddingLeft: this.props.paddingLeft,
} },
this.renderPlaceholder(),
this.renderImage()));
}
renderNonTouchableBlock() {
return (React.createElement(View, { style: {
flex: this.props.flex,
alignContent: 'center',
alignItems: 'center',
alignSelf: this.props.alignSelf,
marginTop: this.props.marginTop,
marginRight: this.props.marginRight,
marginBottom: this.props.marginBottom,
marginLeft: this.props.marginLeft,
paddingTop: this.props.paddingTop,
paddingRight: this.props.paddingRight,
paddingBottom: this.props.paddingBottom,
paddingLeft: this.props.paddingLeft,
}, onLayout: this.props.onLayout },
this.renderPlaceholder(),
this.renderImage()));
}
renderPlaceholder() {
if (this.state.loaded && !UrlUtils.isDeepLink(this.props.url)) {
return undefined;
}
let source = this.props.mode === 'avatar' ?
require('../../Assets/Images/Placeholders/avatar_default.png') :
require('../../Assets/Images/Placeholders/image_default.png');
return (React.createElement(Image, { source: source, accessible: !!this.props.alt, accessibilityLabel: this.props.alt, style: [
{
maxHeight: '100%',
maxWidth: '100%',
width: this.props.width,
height: this.props.height,
},
this.borderRadius,
this.props.style
], resizeMethod: 'resize', resizeMode: 'contain' }));
}
renderImage() {
if (UrlUtils.isSvgXml(this.props.url)) {
return (React.createElement(Svg, { url: this.props.url, width: this.props.width, height: this.props.height, style: [
this.borderRadius,
this.props.style
] }));
}
else {
if (UrlUtils.isDeepLink(this.props.url)) {
return undefined;
}
return (React.createElement(Image, { source: { uri: this.props.url }, accessible: !!this.props.alt, accessibilityLabel: this.props.alt, style: [
{
maxHeight: '100%',
maxWidth: '100%',
width: this.props.width,
height: this.props.height,
},
this.borderRadius,
this.props.style
], resizeMethod: 'resize', resizeMode: 'contain', onError: this.onError }));
}
}
get borderRadius() {
if (this.props.mode === 'avatar') {
return {
borderRadius: this.props.width / 2,
};
}
else {
return {};
}
}
}

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

@ -0,0 +1,15 @@
import React from 'react';
import { View } from 'react-native';
import { StyleManager } from '../../Styles/StyleManager';
export class SeparateLine extends React.PureComponent {
render() {
return (React.createElement(View, { style: {
backgroundColor: StyleManager.separatorColor,
height: StyleManager.separatorThickness,
marginTop: StyleManager.separatorSpacing,
marginRight: StyleManager.separatorSpacing,
marginBottom: StyleManager.separatorSpacing,
marginLeft: StyleManager.separatorSpacing
} }));
}
}

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

@ -0,0 +1,45 @@
import * as React from 'react';
import { WebView } from 'react-native';
export class Svg extends React.Component {
constructor(props) {
super(props);
}
render() {
return (React.createElement(WebView, { source: { html: this.html }, scalesPageToFit: true, scrollEnabled: false, style: [
{
width: this.props.width,
height: this.props.height,
maxWidth: this.props.width,
maxHeight: this.props.height,
backgroundColor: 'transparent',
},
this.props.style
] }));
}
get html() {
return (`<html>
<head>
</head>
<body
style="margin:0; padding:0; overflow:hidden; background-color: 'transparent'; height:100%; width:100%;"
>
<img src="${this.src}"
alt="${this.alt}"
style="position:fixed; top:0; left:0; background-color: 'transparent'; height:100%; width:100%;"
/>
</body>
</html>`);
}
get src() {
if (this.props.url) {
return this.props.url.replace('"', '\'');
}
return '';
}
get alt() {
if (this.props.alt) {
return this.props.alt.replace('"', '\'');
}
return 'Image';
}
}

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

@ -0,0 +1,40 @@
import * as React from 'react';
import { DeviceEventEmitter, Platform, TouchableNativeFeedback, TouchableOpacity, View } from 'react-native';
import { Guid } from '../../Shared/Guid';
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
t[p[i]] = s[p[i]];
return t;
};
export class Touchable extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
if (Platform.OS === 'android') {
DeviceEventEmitter.addListener('KeyEnter' + this.props.testId, this.props.onPress);
}
}
componentWillUnmount() {
if (Platform.OS === 'android') {
DeviceEventEmitter.removeListener('KeyEnter' + this.props.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 },
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));
}
}
get uniqueTestId() {
return this.props.testId + Guid.newGuid();
}
}

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

@ -0,0 +1,19 @@
import * as React from 'react';
import { View } from 'react-native';
import { StyleManager } from '../../Styles/StyleManager';
export class ButtonGroup extends React.Component {
render() {
return (React.createElement(View, { style: {
flexDirection: this.flexDirection,
alignSelf: 'stretch',
marginTop: StyleManager.actionSetSpacing,
paddingTop: 12,
justifyContent: 'center',
borderTopWidth: StyleManager.separatorThickness,
borderTopColor: StyleManager.separatorColor
} }, this.props.children));
}
get flexDirection() {
return StyleManager.actionDirection === 'vertically' ? 'column' : 'row';
}
}

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

@ -0,0 +1,58 @@
import * as React from 'react';
import { Platform, View } from 'react-native';
import { StyleManager } from '../../Styles/StyleManager';
import { ImageBackground } from '../Basic/ImageBackground';
export class Card extends React.Component {
render() {
return (React.createElement(View, { style: [{
flex: this.props.flex,
backgroundColor: '#fff',
borderRadius: 4,
borderWidth: 1,
borderColor: 'rgba(0, 0, 0, .05)',
elevation: 2,
}, Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 0 },
shadowRadius: 3,
shadowOpacity: .08,
},
android: {
elevation: 2,
}
}), this.props.style
] }, this.renderCardContent()));
}
renderCardContent() {
if (this.props.backgroundImageUrl) {
return (React.createElement(ImageBackground, { url: this.props.backgroundImageUrl, containerStyle: {
flex: this.contentFlex,
backgroundColor: StyleManager.getBackgroundColor(this.props.theme),
borderRadius: 4,
overflow: 'hidden',
}, imageStyle: { borderRadius: 4 } },
React.createElement(View, { style: { flex: 1, padding: 0, minHeight: 150 } }, this.props.children)));
}
else {
return (React.createElement(View, { style: {
flex: this.contentFlex,
backgroundColor: StyleManager.getBackgroundColor(this.props.theme),
paddingTop: 12,
paddingRight: 12,
paddingBottom: 12,
paddingLeft: 12,
borderRadius: 4,
overflow: 'hidden'
} }, this.props.children));
}
}
get contentFlex() {
if (this.props.fit === 'container') {
return 1;
}
else {
return 0;
}
}
}

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

@ -0,0 +1,32 @@
import * as React from 'react';
import { Modal, TouchableWithoutFeedback, View } from 'react-native';
export class ModalBox extends React.Component {
constructor(props) {
super(props);
}
render() {
return (React.createElement(Modal, { visible: this.props.show, animationType: 'fade', transparent: true, onRequestClose: this.props.onRequestClose },
React.createElement(TouchableWithoutFeedback, { onPress: this.props.onPressBackground },
React.createElement(View, { style: [
{
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
backgroundColor: 'rgba(0, 0, 0, 0.3)'
}
] })),
React.createElement(View, { style: {
flex: 1,
justifyContent: 'center',
alignContent: 'center',
alignItems: 'center'
} },
React.createElement(View, { style: [
{
width: '85%',
}
] }, this.props.children))));
}
}

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

@ -0,0 +1,86 @@
import * as React from 'react';
import { Text, View } from 'react-native';
import { ImageBlock } from '../Basic/ImageBlock';
import { Touchable } from '../Basic/Touchable';
export class Button extends React.Component {
constructor(props) {
super(props);
}
render() {
return (React.createElement(Touchable, { testId: this.props.title, onPress: this.props.onPress, accessibilityLabel: 'Button ' + this.props.title, style: [
{
flex: this.props.flex,
width: this.props.width,
height: this.props.height,
marginTop: this.props.marginTop,
marginRight: this.props.marginRight,
marginBottom: this.props.marginBottom,
marginLeft: this.props.marginLeft,
paddingTop: this.props.paddingTop,
paddingRight: this.props.paddingRight,
paddingBottom: this.props.paddingBottom,
paddingLeft: this.props.paddingLeft,
backgroundColor: this.props.backgroundColor,
borderColor: this.props.borderColor,
borderRadius: this.props.borderRadius,
borderWidth: this.props.borderWidth,
}, this.props.style
] },
React.createElement(View, { style: {
flexDirection: this.layoutDirection,
flex: 0,
justifyContent: this.props.textVerticalAlign,
}, pointerEvents: 'none' },
this.renderIcon(),
this.renderTitle())));
}
renderIcon() {
if (this.props.icon && this.props.icon.url) {
if (this.props.icon.type === 'img') {
return (React.createElement(ImageBlock, { url: this.props.icon.url, width: this.props.icon.width, height: this.props.icon.height, marginTop: this.props.icon.marginTop, marginRight: this.props.icon.marginRight, marginBottom: this.props.icon.marginBottom, marginLeft: this.props.icon.marginLeft }));
}
else {
return (React.createElement(Text, { style: {
textAlign: 'center',
color: this.props.icon.color,
fontSize: this.props.icon.width,
fontFamily: this.props.icon.fontFamily,
lineHeight: this.props.icon.height,
marginTop: this.props.icon.marginTop,
marginRight: this.props.icon.marginRight,
marginBottom: this.props.icon.marginBottom,
marginLeft: this.props.icon.marginLeft,
} }, this.props.icon.url));
}
}
return undefined;
}
renderTitle() {
return (React.createElement(Text, { accessible: false, adjustsFontSizeToFit: true, allowFontScaling: true, numberOfLines: 1, style: {
flex: this.props.flex,
color: this.props.color,
fontFamily: this.props.fontFamily,
fontSize: this.props.fontSize,
fontWeight: this.props.fontWeight,
flexWrap: this.props.wrap,
textAlign: this.props.textHorizontalAlign,
} }, this.props.title));
}
get layoutDirection() {
if (this.props.icon) {
switch (this.props.icon.position) {
case 'top':
return 'column';
case 'bottom':
return 'column-reverse';
case 'left':
return 'row';
case 'right':
return 'row-reverse';
default:
return 'row';
}
}
return 'row';
}
}

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

@ -0,0 +1,79 @@
import * as React from 'react';
import { DatePickerAndroid, DatePickerIOS, Platform } from 'react-native';
import { StyleManager } from '../../Styles/StyleManager';
import { TimeUtils } from '../../Utils/TimeUtils';
import { ButtonGroup } from '../Containers/ButtonGroup';
import { Card } from '../Containers/Card';
import { ModalBox } from '../Containers/ModalBox';
import { Button } from './Button';
export class DatePanel extends React.Component {
constructor() {
super(...arguments);
this.onCancel = () => {
if (this.props.onCancel) {
this.props.onCancel();
}
};
this.onSave = () => {
if (this.props.onSave) {
this.props.onSave();
}
};
this.onDateChange = (date) => {
if (this.props.onValueChange) {
this.props.onValueChange(TimeUtils.getDateString(date));
}
};
}
componentDidUpdate(prevProps) {
if (Platform.OS === 'android' && this.props.show && !prevProps.show) {
this.showPickerAndroid();
}
}
render() {
if (Platform.OS === 'ios') {
return (React.createElement(ModalBox, { show: this.show },
React.createElement(Card, { flex: 0, fit: 'content' },
React.createElement(DatePickerIOS, { date: TimeUtils.extractDate(this.props.value), mode: 'date', onDateChange: this.onDateChange }),
React.createElement(ButtonGroup, null,
this.renderCancelButton(),
this.renderSaveButton()))));
}
return null;
}
renderCancelButton() {
return (React.createElement(Button, { flex: 1, title: 'Cancel', color: StyleManager.getColor('accent', 'default', false), fontSize: StyleManager.getFontSize('default'), fontWeight: StyleManager.getFontWeight('bolder'), backgroundColor: StyleManager.getBackgroundColor('default'), textHorizontalAlign: 'center', textVerticalAlign: 'center', paddingTop: 6, paddingBottom: 6, paddingLeft: 16, paddingRight: 16, onPress: this.onCancel }));
}
renderSaveButton() {
return (React.createElement(Button, { flex: 1, title: 'Save', color: StyleManager.getColor('accent', 'default', false), fontSize: StyleManager.getFontSize('default'), fontWeight: StyleManager.getFontWeight('bolder'), backgroundColor: StyleManager.getBackgroundColor('default'), textHorizontalAlign: 'center', textVerticalAlign: 'center', paddingTop: 6, paddingBottom: 6, paddingLeft: 16, paddingRight: 16, onPress: this.onSave, style: {
borderLeftWidth: 1,
borderLeftColor: StyleManager.separatorColor,
} }));
}
async showPickerAndroid() {
if (Platform.OS === 'android') {
const today = TimeUtils.extractDate(this.props.value);
try {
const { action, year, month, day } = await DatePickerAndroid.open({
date: today,
});
if (action === DatePickerAndroid.dateSetAction) {
let newDate = new Date(year, month, day);
this.onDateChange(newDate);
this.onSave();
}
if (action === DatePickerAndroid.dismissedAction) {
this.setState({
showDatePicker: false
}, this.onCancel);
}
}
catch ({ code, message }) {
console.warn('Cannot open date picker', message);
}
}
}
get show() {
return this.props.show && Platform.OS === 'ios';
}
}

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

@ -0,0 +1,116 @@
import * as React from 'react';
import { TextInput, } from 'react-native';
import { StyleManager } from '../../Styles/StyleManager';
export class InputBox extends React.Component {
constructor(props) {
super(props);
this.onValueChange = (value) => {
if (this.props.onValueChange) {
this.props.onValueChange(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: false,
};
}
render() {
return (React.createElement(TextInput, { style: [
{
flex: this.props.flex,
color: this.color,
fontSize: this.fontSize,
fontWeight: this.fontWeight,
backgroundColor: this.backgroundColor,
width: this.props.width,
height: this.props.height || this.height,
borderColor: this.borderColor,
borderWidth: 1,
borderRadius: 4,
marginTop: this.props.marginTop,
marginRight: this.props.marginRight,
marginBottom: this.props.marginBottom,
marginLeft: this.props.marginLeft,
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 }));
}
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 numberOfLine() {
if (this.props.numberOfLines && this.props.numberOfLines > 0) {
return this.props.numberOfLines;
}
return 1;
}
get height() {
return this.fontSize * this.numberOfLine + this.paddingVertical * 2;
}
get color() {
if (this.state.focused) {
return StyleManager.getInputFocusColor(this.props.theme);
}
else {
return StyleManager.getInputColor(this.props.theme);
}
}
get backgroundColor() {
if (this.state.focused) {
return StyleManager.getInputFocusBackgroundColor(this.props.theme);
}
else {
return StyleManager.getInputBackgroundColor(this.props.theme);
}
}
get borderColor() {
if (this.state.focused) {
return StyleManager.getInputFocusBorderColor(this.props.theme);
}
else {
return StyleManager.getInputBorderColor(this.props.theme);
}
}
}

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

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

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

@ -0,0 +1,37 @@
import { HostConfig } from './Types';
export class ConfigManager {
constructor() { }
static getInstance() {
if (ConfigManager.sharedInstance === undefined) {
ConfigManager.sharedInstance = new ConfigManager();
}
return ConfigManager.sharedInstance;
}
static loadConfig() {
try {
let rawConfig;
rawConfig = require('./default.json');
return new HostConfig(rawConfig);
}
catch (error) {
console.error(error);
return undefined;
}
}
static parseConfig(json) {
return new HostConfig(json);
}
getDefaultConfig() {
if (this.defaultConfig === undefined) {
this.defaultConfig = ConfigManager.loadConfig();
}
return this.defaultConfig;
}
getConfig(override) {
let config = this.getDefaultConfig();
if (config) {
return config.combine(override);
}
return config;
}
}

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

@ -0,0 +1,537 @@
export class SpacingConfig {
constructor(json) {
if (json) {
this.default = json['default'];
this.small = json['small'];
this.medium = json['medium'];
this.large = json['large'];
this.extraLarge = json['extraLarge'];
this.padding = json['padding'];
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
default: current.default !== undefined ? current.default : prev.default,
small: current.small !== undefined ? current.small : prev.small,
medium: current.medium !== undefined ? current.medium : prev.medium,
large: current.large !== undefined ? current.large : prev.large,
extraLarge: current.extraLarge !== undefined ? current.extraLarge : prev.extraLarge,
padding: current.padding !== undefined ? current.padding : prev.padding,
};
}
return prev;
}, this);
}
return this;
}
}
export class SeparatorConfig {
constructor(json) {
if (json) {
this.thickness = json['lineThickness'];
this.color = json['lineColor'];
this.spacing = json['spacing'];
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
thickness: current.thickness !== undefined ? current.thickness : prev.thickness,
color: current.color !== undefined ? current.color : prev.color,
spacing: current.spacing !== undefined ? current.spacing : prev.spacing,
};
}
return prev;
}, this);
}
return this;
}
}
export class FontSizeConfig {
constructor(json) {
if (json) {
this.default = json['default'];
this.small = json['small'];
this.medium = json['medium'];
this.large = json['large'];
this.extraLarge = json['extraLarge'];
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
default: current.default !== undefined ? current.default : prev.default,
small: current.small !== undefined ? current.small : prev.small,
medium: current.medium !== undefined ? current.medium : prev.medium,
large: current.large !== undefined ? current.large : prev.large,
extraLarge: current.extraLarge !== undefined ? current.extraLarge : prev.extraLarge,
};
}
return prev;
}, this);
}
return this;
}
}
export class FontWeightConfig {
constructor(json) {
if (json) {
this.default = json['default'] + '';
this.lighter = json['lighter'] + '';
this.bolder = json['bolder'] + '';
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
default: current.default !== undefined ? current.default : prev.default,
lighter: current.lighter !== undefined ? current.lighter : prev.lighter,
bolder: current.bolder !== undefined ? current.bolder : prev.bolder,
};
}
return prev;
}, this);
}
return this;
}
}
export class ColorConfig {
constructor(json) {
if (json) {
this.default = json['default'] || this.default;
this.subtle = json['subtle'] || this.subtle;
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
default: current.default !== undefined ? current.default : prev.default,
subtle: current.subtle !== undefined ? current.subtle : prev.subtle,
};
}
return prev;
}, this);
}
return this;
}
}
export class ColorSetConfig {
constructor(json) {
if (json) {
this.default = new ColorConfig(json['default']);
this.dark = new ColorConfig(json['dark']);
this.light = new ColorConfig(json['light']);
this.accent = new ColorConfig(json['accent']);
this.attention = new ColorConfig(json['attention']);
this.good = new ColorConfig(json['good']);
this.warning = new ColorConfig(json['warning']);
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
default: prev.default !== undefined ? prev.default.combine(current.default) : current.default,
dark: prev.default !== undefined ? prev.dark.combine(current.dark) : current.dark,
light: prev.light !== undefined ? prev.light.combine(current.light) : current.light,
accent: prev.accent !== undefined ? prev.accent.combine(current.accent) : current.accent,
attention: prev.attention !== undefined ? prev.attention.combine(current.attention) : current.attention,
good: prev.good !== undefined ? prev.good.combine(current.good) : current.good,
warning: prev.warning !== undefined ? prev.warning.combine(current.warning) : current.warning,
};
}
return prev;
}, this);
}
return this;
}
}
export class ThemeConfig {
constructor(json) {
if (json) {
this.background = json['backgroundColor'];
this.foreground = new ColorSetConfig(json['foregroundColors']);
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
background: current.background !== undefined ? current.background : prev.background,
foreground: prev.foreground !== undefined ? prev.foreground.combine(current.foreground) : current.foreground,
};
}
return prev;
}, this);
}
return this;
}
}
export class ContainerConfig {
constructor(json) {
if (json) {
this.default = new ThemeConfig(json['default']);
this.emphasis = new ThemeConfig(json['emphasis']);
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
default: prev.default !== undefined ? prev.default.combine(current.default) : current.default,
emphasis: prev.emphasis !== undefined ? prev.emphasis.combine(current.emphasis) : current.emphasis,
};
}
return prev;
}, this);
}
return this;
}
}
export class ImageSizeConfig {
constructor(json) {
if (json) {
this.small = json['small'];
this.medium = json['medium'];
this.large = json['large'];
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
small: current.small !== undefined ? current.small : prev.small,
medium: current.medium !== undefined ? current.medium : prev.medium,
large: current.large !== undefined ? current.large : prev.large,
};
}
return prev;
}, this);
}
return this;
}
}
export class ShowCardActionConfig {
constructor(json) {
if (json) {
this.mode = json['actionMode'];
this.margin = json['inlineTopMargin'];
this.style = json['style'];
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
mode: current.mode !== undefined ? current.mode : prev.mode,
margin: current.margin !== undefined ? current.margin : prev.margin,
style: current.style !== undefined ? current.style : prev.style,
};
}
return prev;
}, this);
}
return this;
}
}
export class ActionConfig {
constructor(json) {
if (json) {
this.capacity = json['maxActions'];
this.actionSetSpacing = json['spacing'];
this.actionSpacing = json['buttonSpacing'];
this.showCard = new ShowCardActionConfig(json['showCard']);
this.cardExpanding = json['preExpandSingleShowCardAction'];
this.direction = json['actionsOrientation'];
this.align = json['actionAlignment'];
this.iconPosition = json['iconPlacement'];
this.iconSize = json['iconSize'];
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
capacity: current.capacity !== undefined ? current.capacity : prev.capacity,
actionSetSpacing: current.actionSetSpacing !== undefined ? current.actionSetSpacing : prev.actionSetSpacing,
actionSpacing: current.actionSpacing !== undefined ? current.actionSpacing : prev.actionSpacing,
showCard: prev.showCard !== undefined ? prev.showCard.combine(current.showCard) : current.showCard,
cardExpanding: current.cardExpanding !== undefined ? current.cardExpanding : prev.cardExpanding,
direction: current.direction !== undefined ? current.direction : prev.direction,
align: current.align !== undefined ? current.align : prev.align,
iconPosition: current.iconPosition !== undefined ? current.iconPosition : prev.iconPosition,
iconSize: current.iconSize !== undefined ? current.iconSize : prev.iconSize,
};
}
return prev;
}, this);
}
return this;
}
}
export class CardConfig {
constructor(json) {
if (json) {
this.allowCustomStyle = json['allowCustomStyle'];
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
allowCustomStyle: current.allowCustomStyle !== undefined ? current.allowCustomStyle : prev.allowCustomStyle,
};
}
return prev;
}, this);
}
return this;
}
}
export class ImageSetConfig {
constructor(json) {
if (json) {
this.imageSize = json['imageSize'];
this.maxImageHeight = json['maxImageHeight'];
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
imageSize: current.imageSize !== undefined ? current.imageSize : prev.imageSize,
maxImageHeight: current.maxImageHeight !== undefined ? current.maxImageHeight : prev.maxImageHeight,
};
}
return prev;
}, this);
}
return this;
}
}
export class FactValueConfig {
constructor(json) {
if (json) {
this.size = json['size'];
this.color = json['color'];
this.isSubtle = json['isSubtle'];
this.weight = json['weight'];
this.wrap = json['wrap'];
}
return this;
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
size: current.size !== undefined ? current.size : prev.size,
color: current.color !== undefined ? current.color : prev.color,
isSubtle: current.isSubtle !== undefined ? current.isSubtle : prev.isSubtle,
weight: current.weight !== undefined ? current.weight : prev.weight,
wrap: current.wrap !== undefined ? current.wrap : prev.wrap,
};
}
return prev;
}, this);
}
return this;
}
}
export class FactTitleConfig {
constructor(json) {
if (json) {
this.size = json['size'];
this.color = json['color'];
this.isSubtle = json['isSubtle'];
this.weight = json['weight'];
this.wrap = json['wrap'];
this.maxWidth = json['maxWidth'];
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
size: current.size !== undefined ? current.size : prev.size,
color: current.color !== undefined ? current.color : prev.color,
isSubtle: current.isSubtle !== undefined ? current.isSubtle : prev.isSubtle,
weight: current.weight !== undefined ? current.weight : prev.weight,
wrap: current.wrap !== undefined ? current.wrap : prev.wrap,
maxWidth: current.maxWidth !== undefined ? current.maxWidth : prev.maxWidth,
};
}
return prev;
}, this);
}
return this;
}
}
export class FactSetConfig {
constructor(json) {
if (json) {
this.title = new FactTitleConfig(json['title']);
this.value = new FactValueConfig(json['value']);
this.margin = json['spacing'];
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
title: prev.title !== undefined ? prev.title.combine(current.title) : current.title,
value: prev.value !== undefined ? prev.value.combine(current.value) : current.value,
margin: current.margin !== undefined ? current.margin : prev.margin,
};
}
return prev;
}, this);
}
return this;
}
}
export class InputThemeConfig {
constructor(json) {
if (json) {
this.color = json['color'];
this.focusColor = json['focusColor'];
this.backgroundColor = json['backgroundColor'];
this.focusBackgroundColor = json['focusBackgroundColor'];
this.borderColor = json['borderColor'];
this.focusBorderColor = json['focusBorderColor'];
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
color: current.color !== undefined ? current.color : prev.color,
focusColor: current.focusColor !== undefined ? current.focusColor : prev.focusColor,
backgroundColor: current.backgroundColor !== undefined ? current.backgroundColor : prev.backgroundColor,
focusBackgroundColor: current.focusBackgroundColor !== undefined ? current.focusBackgroundColor : prev.focusBackgroundColor,
borderColor: current.borderColor !== undefined ? current.borderColor : prev.borderColor,
focusBorderColor: current.focusBorderColor !== undefined ? current.focusBorderColor : prev.focusBorderColor,
};
}
return prev;
}, this);
}
return this;
}
}
export class InputConfig {
constructor(json) {
if (json) {
this.default = new InputThemeConfig(json['default']);
this.emphasis = new InputThemeConfig(json['emphasis']);
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
default: prev.default !== undefined ? prev.default.combine(current.default) : current.default,
emphasis: prev.emphasis !== undefined ? prev.emphasis.combine(current.emphasis) : current.emphasis,
};
}
return prev;
}, this);
}
return this;
}
}
export class MediaConfig {
constructor(json) {
if (json) {
this.defaultPosterUrl = json['defaultPoster'];
this.playButtonUrl = json['playButton'];
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
defaultPosterUrl: current.defaultPosterUrl !== undefined ? current.defaultPosterUrl : prev.defaultPosterUrl,
playButtonUrl: current.playButtonUrl !== undefined ? current.playButtonUrl : prev.playButtonUrl,
};
}
return prev;
}, this);
}
return this;
}
}
export class HostConfig {
constructor(json) {
if (json) {
this.spacing = new SpacingConfig(json['spacing']);
this.separator = new SeparatorConfig(json['separator']);
this.supportInteractive = json['supportsInteractivity'];
this.fontFamily = json['fontFamily'];
this.fontSize = new FontSizeConfig(json['fontSizes']);
this.fontWeight = new FontWeightConfig(json['fontWeights']);
this.container = new ContainerConfig(json['containerStyles']);
this.imageSize = new ImageSizeConfig(json['imageSizes']);
this.action = new ActionConfig(json['actions']);
this.card = new CardConfig(json['adaptiveCard']);
this.imageSet = new ImageSetConfig(json['imageSet']);
this.factSet = new FactSetConfig(json['factSet']);
this.media = new MediaConfig(json['media']);
this.input = new InputConfig(json['input']);
this.mode = json['mode'];
}
}
combine(...args) {
if (args) {
return args.reduce((prev, current) => {
if (current) {
return {
spacing: prev.spacing !== undefined ? prev.spacing.combine(current.spacing) : current.spacing,
separator: prev.separator !== undefined ? prev.separator.combine(current.separator) : current.separator,
supportInteractive: current.supportInteractive !== undefined ? current.supportInteractive : prev.supportInteractive,
fontFamily: current.fontFamily !== undefined ? current.fontFamily : prev.fontFamily,
fontSize: prev.fontSize !== undefined ? prev.fontSize.combine(current.fontSize) : current.fontSize,
fontWeight: prev.fontWeight !== undefined ? prev.fontWeight.combine(current.fontWeight) : current.fontWeight,
container: prev.container !== undefined ?
prev.container.combine(current.container) :
current.container,
imageSize: prev.imageSize !== undefined ? prev.imageSize.combine(current.imageSize) : current.imageSize,
action: prev.action !== undefined ? prev.action.combine(current.action) : current.action,
card: prev.card !== undefined ? prev.card.combine(current.card) : current.card,
imageSet: prev.imageSet !== undefined ? prev.imageSet.combine(current.imageSet) : current.imageSet,
factSet: prev.factSet !== undefined ? prev.factSet.combine(current.factSet) : current.factSet,
media: prev.media !== undefined ? prev.media.combine(current.media) : current.media,
input: prev.input !== undefined ? prev.input.combine(current.input) : current.input,
mode: current.mode !== undefined ? current.mode : prev.mode,
};
}
return prev;
}, this);
}
return this;
}
}

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

@ -0,0 +1,159 @@
{
"mode": "debug",
"choiceSetInputValueSeparator": ",",
"supportsInteractivity": true,
"fontFamily": "Roboto",
"spacing": {
"small": 3,
"default": 8,
"medium": 20,
"large": 30,
"extraLarge": 40,
"padding": 10
},
"separator": {
"lineThickness": 1,
"lineColor": "#00000015",
"spacing": 12
},
"fontSizes": {
"small": 12,
"default": 14,
"medium": 17,
"large": 21,
"extraLarge": 26
},
"fontWeights": {
"lighter": 200,
"default": 400,
"bolder": 500
},
"imageSizes": {
"small": 12,
"medium": 36,
"large": 64
},
"containerStyles": {
"default": {
"foregroundColors": {
"default": {
"default": "#000000DE",
"subtle": "#0000008A"
},
"dark": {
"default": "#000000",
"subtle": "#00000066"
},
"light": {
"default": "#FFFFFF",
"subtle": "#FFFFFF33"
},
"accent": {
"default": "#006ae2",
"subtle": "#006ae2b2"
},
"good": {
"default": "#048e2e",
"subtle": "#048e2eb2"
},
"warning": {
"default": "#eb7b07",
"subtle": "#eb7b07b2"
},
"attention": {
"default": "#c80000",
"subtle": "#c80000b2"
}
},
"backgroundColor": "#FFFFFF00"
},
"emphasis": {
"foregroundColors": {
"default": {
"default": "#000000",
"subtle": "#00000066"
},
"dark": {
"default": "#000000",
"subtle": "#00000066"
},
"light": {
"default": "#FFFFFF",
"subtle": "#FFFFFF33"
},
"accent": {
"default": "#006ae2",
"subtle": "#006ae2b2"
},
"good": {
"default": "#048e2e",
"subtle": "#048e2eb2"
},
"warning": {
"default": "#eb7b07",
"subtle": "#eb7b07b2"
},
"attention": {
"default": "#c80000",
"subtle": "#c80000b2"
}
},
"backgroundColor": "#FFFFFF00"
}
},
"actions": {
"maxActions": 2,
"spacing": "large",
"buttonSpacing": 0,
"showCard": {
"actionMode": "inline",
"inlineTopMargin": 16,
"style": "emphasis"
},
"preExpandSingleShowCardAction": false,
"actionsOrientation": "horizontal",
"actionAlignment": "left"
},
"adaptiveCard": {
"allowCustomStyle": false
},
"imageSet": {
"imageSize": "medium",
"maxImageHeight": 100
},
"factSet": {
"title": {
"size": "default",
"color": "default",
"isSubtle": false,
"weight": "bolder",
"warp": true
},
"value": {
"size": "default",
"color": "default",
"isSubtle": false,
"weight": "default",
"warp": true
},
"spacing": 10
},
"input": {
"default": {
"color": "#000000",
"focusColor": "#000000",
"backgroundColor": "#F3F3F3",
"focusBackgroundColor": "#FFFFFF",
"borderColor": "#F3F3F3",
"focusBorderColor": "#006ae2"
},
"emphasis": {
"color": "#000000",
"focusColor": "#000000",
"backgroundColor": "#FFFFFF",
"focusBackgroundColor": "#FFFFFF",
"borderColor": "#FFFFFF",
"focusBorderColor": "#006ae2"
}
}
}

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

@ -0,0 +1,71 @@
import { HostContext } from './HostContext';
export class ActionContext {
constructor() {
this.hooks = {};
}
static createInstance() {
return new ActionContext();
}
static getGlobalInstance() {
if (ActionContext.sharedInstance === undefined) {
ActionContext.sharedInstance = ActionContext.createInstance();
}
return ActionContext.sharedInstance;
}
static clearGlobalInstance() {
ActionContext.sharedInstance = undefined;
}
registerHook(hook) {
if (hook) {
if (this.hooks[hook.actionType] === undefined) {
this.hooks[hook.actionType] = [];
}
if (!this.hooks[hook.actionType].some(value => value.name === hook.name)) {
this.hooks[hook.actionType].push(hook);
}
}
}
getHooks(actionType) {
if (actionType) {
return this.hooks[actionType];
}
return [];
}
getActionEventHandler(target, onFinish, onError) {
return ((...externalHooks) => {
let action = target.action;
if (action) {
let callback = HostContext.getInstance().getHandler(action.type);
let args = {
action: action,
formValidate: false,
onFinishCallback: onFinish,
onErrorCallback: onError,
};
let hookFuncs = this.getExecuteFuncs(action.type, externalHooks);
args = hookFuncs.reduce((prev, current) => {
return current(prev);
}, args);
if (callback && typeof callback === 'function') {
callback(args);
}
}
});
}
getExecuteFuncs(actionType, externalHooks) {
let hookFuncs = [];
if (this.hooks) {
let hookArrays = this.hooks[actionType];
if (hookArrays === undefined) {
hookArrays = [];
}
if (externalHooks) {
hookArrays = hookArrays.concat(externalHooks);
}
hookFuncs = hookFuncs.concat(hookArrays.reduce((prev, current) => {
return prev.concat(current.func);
}, []));
}
return hookFuncs;
}
}

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

@ -0,0 +1,83 @@
export class FormContext {
constructor() {
this.formFields = {};
}
static getInstance() {
if (FormContext.sharedInstance === undefined) {
FormContext.sharedInstance = new FormContext();
}
return FormContext.sharedInstance;
}
updateField(id, value, validate) {
if (id && value) {
this.formFields[id] = {
value: value,
validate: validate
};
}
}
getField(id) {
if (id) {
return this.formFields[id];
}
return undefined;
}
getFieldValue(id) {
let field = this.getField(id);
if (field) {
return field.value;
}
return undefined;
}
getFields(ids) {
let result = [];
if (ids) {
ids.forEach((id) => {
result.push(this.getField(id));
});
}
return result;
}
getFormData(ids) {
if (ids) {
return ids.reduce((prev, id) => {
let field = this.formFields[id];
let result = {};
if (field && field.value) {
result[id] = field.value;
}
else {
result[id] = '';
}
return Object.assign({}, prev, result);
}, {});
}
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) {
return field.validate;
}
return true;
}
validateFields(ids) {
if (ids) {
return ids.reduce((prev, current) => {
return prev && this.validateField(current);
}, true);
}
return true;
}
}

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

@ -0,0 +1,80 @@
import { ConfigManager } from '../Config/ConfigManager';
import { HostConfig } from '../Config/Types';
import { ActionType } from '../Schema/Abstract/ActionElement';
export class HostContext {
constructor() {
this.config = ConfigManager.getInstance().getDefaultConfig();
}
static getInstance() {
if (HostContext.sharedInstance === undefined) {
HostContext.sharedInstance = new HostContext();
}
return HostContext.sharedInstance;
}
registerErrorHandler(handler) {
this.onError = handler;
}
registerInfoHandler(handler) {
this.onInfo = handler;
}
registerWarningHandler(handler) {
this.onWarning = handler;
}
registerFocusHandler(handler) {
this.onFocus = handler;
}
registerBlurHandler(handler) {
this.onBlur = handler;
}
registerOpenUrlHandler(handler) {
this.onOpenUrl = handler;
}
registerShowCardHandler(handler) {
this.onShowCard = handler;
}
registerSubmitHandler(handler) {
this.onSubmit = handler;
}
registerCallbackHandler(handler) {
this.onCallback = handler;
}
applyConfig(configJson) {
this.config.combine(new HostConfig(configJson));
}
getConfig() {
return this.config;
}
getHandler(type) {
let callback;
switch (type) {
case ActionType.OpenUrl:
callback = this.onOpenUrl;
break;
case ActionType.Callback:
callback = this.onCallback;
break;
case ActionType.ShowCard:
callback = this.onShowCard;
break;
case ActionType.Submit:
callback = this.onSubmit;
break;
case 'focus':
callback = this.onFocus;
break;
case 'blur':
callback = this.onBlur;
break;
case 'error':
callback = this.onError;
break;
case 'info':
callback = this.onInfo;
break;
case 'warning':
callback = this.onWarning;
break;
}
return callback;
}
}

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

@ -0,0 +1,30 @@
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];
}
}

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

@ -0,0 +1,54 @@
import { ConsoleUtils } from '../../Utils/ConsoleUtils';
import { JsonUtils } from '../../Utils/JsonUtils';
export var CardElementType;
(function (CardElementType) {
CardElementType["Column"] = "Column";
CardElementType["ColumnSet"] = "ColumnSet";
CardElementType["Container"] = "Container";
CardElementType["Fact"] = "Fact";
CardElementType["FactSet"] = "FactSet";
CardElementType["Image"] = "Image";
CardElementType["ImageSet"] = "ImageSet";
CardElementType["TextBlock"] = "TextBlock";
CardElementType["TextInput"] = "Input.Text";
CardElementType["NumberInput"] = "Input.Number";
CardElementType["DateInput"] = "Input.Date";
CardElementType["TimeInput"] = "Input.Time";
CardElementType["ToggleInput"] = "Input.Toggle";
CardElementType["ChoiceInput"] = "Input.Choice";
CardElementType["ChoiceSetInput"] = "Input.ChoiceSet";
CardElementType["AdaptiveCard"] = "AdaptiveCard";
})(CardElementType || (CardElementType = {}));
export class AbstractElement {
constructor(json, parent) {
let validation = JsonUtils.isValidateJson(json, this.requiredProperties);
if (!validation.isValid) {
ConsoleUtils.warning('AbstractElement', validation.message);
}
else {
this.isValid = validation.isValid;
this.type = json.type;
this.parent = parent;
}
}
get ancestors() {
if (this.parent) {
return [this.parent, ...this.parent.ancestors];
}
return [];
}
get ancestorsAndSelf() {
return [this, ...this.ancestors];
}
get descends() {
return this.children.reduce((prev, current) => {
return prev.concat(current.descends);
}, this.children.slice());
}
get descendsAndSelf() {
return [this, ...this.descends];
}
get requiredProperties() {
return ['type'];
}
}

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

@ -0,0 +1,19 @@
import { AbstractElement } from './AbstractElement';
export var ActionType;
(function (ActionType) {
ActionType["OpenUrl"] = "Action.OpenUrl";
ActionType["Submit"] = "Action.Submit";
ActionType["ShowCard"] = "Action.ShowCard";
ActionType["Callback"] = "Action.Callback";
})(ActionType || (ActionType = {}));
export class ActionElement extends AbstractElement {
constructor(json, parent) {
super(json, parent);
if (this.isValid) {
this.title = json.title;
}
}
get action() {
return this;
}
}

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

@ -0,0 +1,29 @@
import { AbstractElement } from './AbstractElement';
export var ContentElementType;
(function (ContentElementType) {
ContentElementType["Column"] = "Column";
ContentElementType["ColumnSet"] = "ColumnSet";
ContentElementType["Container"] = "Container";
ContentElementType["FactSet"] = "FactSet";
ContentElementType["Image"] = "Image";
ContentElementType["ImageSet"] = "ImageSet";
ContentElementType["TextBlock"] = "TextBlock";
ContentElementType["TextInput"] = "Input.Text";
ContentElementType["NumberInput"] = "Input.Number";
ContentElementType["DateInput"] = "Input.Date";
ContentElementType["TimeInput"] = "Input.Time";
ContentElementType["ToggleInput"] = "Input.Toggle";
ContentElementType["ChoiceSetInput"] = "Input.ChoiceSet";
ContentElementType["PeoplePicker"] = "Input.PeoplePicker";
ContentElementType["AdaptiveCard"] = "AdaptiveCard";
})(ContentElementType || (ContentElementType = {}));
export class ContentElement extends AbstractElement {
constructor(json, parent) {
super(json, parent);
if (this.isValid) {
this.id = json.id;
this.spacing = json.spacing;
this.separator = json.separator || false;
}
}
}

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

@ -0,0 +1,21 @@
import { ContentElement } from './ContentElement';
export var InputElementType;
(function (InputElementType) {
InputElementType["TextInput"] = "Input.Text";
InputElementType["NumberInput"] = "Input.Number";
InputElementType["DateInput"] = "Input.Date";
InputElementType["TimeInput"] = "Input.Time";
InputElementType["ToggleInput"] = "Input.Toggle";
InputElementType["ChoiceSetInput"] = "Input.ChoiceSet";
InputElementType["PeoplePicker"] = "Input.PeoplePicker";
})(InputElementType || (InputElementType = {}));
export class InputElement extends ContentElement {
constructor(json, parent) {
super(json, parent);
this.children = [];
if (this.isValid) {
this.id = json.id;
this.value = json.value;
}
}
}

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

@ -0,0 +1,50 @@
import { FormContext } from '../../Contexts/FormContext';
import { ConsoleUtils } from '../../Utils/ConsoleUtils';
import { ElementUtils } from '../../Utils/ElementUtils';
import { ActionFactory } from '../Factories/ActionFactory';
import { ContentElement } from './ContentElement';
export var FormElementType;
(function (FormElementType) {
FormElementType["Column"] = "Column";
FormElementType["ColumnSet"] = "ColumnSet";
FormElementType["Container"] = "Container";
FormElementType["Image"] = "Image";
FormElementType["AdaptiveCard"] = "AdaptiveCard";
})(FormElementType || (FormElementType = {}));
export class ScopeElement extends ContentElement {
constructor(json, parent) {
super(json, parent);
if (this.isValid) {
this.backgroundImage = json.backgroundImage;
this.selectAction = ActionFactory.create(json.selectAction, this);
if (this.selectAction) {
if (this.selectAction.type === 'Action.ShowCard') {
ConsoleUtils.error(this.type, 'Do not support Action.ShowCard in selectAction.');
}
}
}
}
get action() {
return this.selectAction;
}
get inputFields() {
return this.descendsAndSelf.reduce((prev, current) => {
if (ElementUtils.isInput(current.type)) {
return prev.concat([current.id]);
}
return prev;
}, []);
}
getBackgroundImageUrl() {
if (this.backgroundImage) {
if (typeof this.backgroundImage === 'string') {
return this.backgroundImage;
}
return this.backgroundImage.url;
}
return undefined;
}
validateScope() {
return FormContext.getInstance().validateFields(this.inputFields);
}
}

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

@ -0,0 +1,18 @@
import { AbstractElement } from './AbstractElement';
export var ValueElementType;
(function (ValueElementType) {
ValueElementType["Fact"] = "Fact";
ValueElementType["ChoiceInput"] = "Input.Choice";
})(ValueElementType || (ValueElementType = {}));
export class ValueElement extends AbstractElement {
constructor(json, parent) {
super(json, parent);
if (this.isValid) {
this.title = json.title;
this.value = json.value;
}
}
get requiredProperties() {
return ['title', 'value'];
}
}

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

@ -0,0 +1,16 @@
import { ActionElement } from '../Abstract/ActionElement';
export class OpenUrlActionElement extends ActionElement {
constructor(json, parent) {
super(json, parent);
this.children = [];
if (this.isValid) {
this.url = json.url;
}
}
get scope() {
return this.ancestorsAndSelf.find(element => element.parent === undefined);
}
get requiredProperties() {
return ['type', 'title', 'url'];
}
}

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

@ -0,0 +1,25 @@
import { ActionElement } from '../Abstract/ActionElement';
import { CardElement } from '../Cards/Card';
export class ShowCardActionElement extends ActionElement {
constructor(json, parent) {
super(json, parent);
if (this.isValid) {
this.card = new CardElement(json.card, parent);
}
}
get scope() {
if (this.parent) {
return this.parent;
}
return undefined;
}
get children() {
if (this.card) {
return [this.card];
}
return [];
}
get requiredProperties() {
return ['type', 'title', 'card'];
}
}

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

@ -0,0 +1,16 @@
import { ActionElement } from '../Abstract/ActionElement';
export class SubmitActionElement extends ActionElement {
constructor(json, parent) {
super(json, parent);
this.children = [];
if (this.isValid) {
this.data = json.data;
}
}
get scope() {
return this.ancestorsAndSelf.find(element => element.parent === undefined);
}
get requiredProperties() {
return ['type', 'title'];
}
}

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

@ -0,0 +1,17 @@
import { ScopeElement } from '../Abstract/ScopeElement';
export class ImageElement extends ScopeElement {
constructor(json, parent) {
super(json, parent);
this.children = [];
if (this.isValid) {
this.url = json.url;
this.altText = json.altText;
this.horizontalAlignment = json.horizontalAlignment;
this.size = json.size;
this.style = json.style;
}
}
get requiredProperties() {
return ['type', 'url'];
}
}

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

@ -0,0 +1,20 @@
import { ContentElement } from '../Abstract/ContentElement';
export class TextBlockElement extends ContentElement {
constructor(json, parent) {
super(json, parent);
this.children = [];
if (this.isValid) {
this.text = json.text;
this.color = json.color;
this.horizontalAlignment = json.horizontalAlignment;
this.isSubtle = json.isSubtle || false;
this.maxLines = json.maxLines;
this.size = json.size;
this.weight = json.weight;
this.wrap = json.wrap || false;
}
}
get requiredProperties() {
return ['type', 'text'];
}
}

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

@ -0,0 +1,33 @@
import { ScopeElement } from '../Abstract/ScopeElement';
import { ActionFactory } from '../Factories/ActionFactory';
import { ContentElementFactory } from '../Factories/ContentElementFactory';
export class CardElement extends ScopeElement {
constructor(json, parent) {
super(json, parent);
if (this.isValid) {
this.version = json.version;
this.minVersion = json.minVersion;
this.fallbackText = json.fallbackText;
this.speak = json.speak;
this.actions = ActionFactory.createSet(json.actions, this);
this.body = ContentElementFactory.createSet(json.body, this);
this.backgroundImage = json.backgroundImage;
}
}
get children() {
let result = [];
if (this.body) {
result = result.concat(this.body);
}
if (this.actions) {
result = result.concat(this.actions);
}
return result;
}
getBackgroundImageUrl() {
return this.backgroundImage;
}
get requiredProperties() {
return ['type', 'version'];
}
}

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

@ -0,0 +1,34 @@
import { ScopeElement } from '../Abstract/ScopeElement';
import { ContentElementFactory } from '../Factories/ContentElementFactory';
export class ColumnElement extends ScopeElement {
constructor(json, parent) {
super(json, parent);
if (this.isValid) {
this.items = ContentElementFactory.createSet(json.items, this);
this.style = json.style;
this.verticalContentAlignment = json.verticalContentAlignment;
if (json.width) {
if (json.width === 'auto' || json.width === 'stretch') {
this.width = json.width;
}
else {
let columnWidth = parseInt(json.width, 10);
if (columnWidth < 0) {
columnWidth = 0;
}
this.width = columnWidth;
}
this.height = json.height;
}
}
}
get children() {
if (this.items) {
return this.items;
}
return [];
}
get requiredProperties() {
return ['type', 'items'];
}
}

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

@ -0,0 +1,27 @@
import { ScopeElement } from '../Abstract/ScopeElement';
import { ColumnElement } from './Column';
export class ColumnSetElement extends ScopeElement {
constructor(json, parent) {
super(json, parent);
if (this.isValid) {
this.columns = [];
if (json.columns) {
json.columns.forEach((item) => {
let column = new ColumnElement(item, this);
if (column && column.isValid) {
this.columns.push(column);
}
});
}
}
}
get children() {
if (this.columns) {
return this.columns;
}
return [];
}
get requiredProperties() {
return ['type'];
}
}

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

@ -0,0 +1,23 @@
import { ScopeElement } from '../Abstract/ScopeElement';
import { ContentElementFactory } from '../Factories/ContentElementFactory';
export class ContainerElement extends ScopeElement {
constructor(json, parent) {
super(json, parent);
this.items = [];
if (this.isValid) {
this.style = json.style;
this.items = ContentElementFactory.createSet(json.items, this);
this.height = json.height;
this.verticalContentAlignment = json.verticalContentAlignment;
}
}
get children() {
if (this.items) {
return this.items;
}
return [];
}
get requiredProperties() {
return ['type', 'items'];
}
}

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

@ -0,0 +1,7 @@
import { ValueElement } from '../Abstract/ValueElement';
export class FactElement extends ValueElement {
constructor(json, parent) {
super(json, parent);
this.children = [];
}
}

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

@ -0,0 +1,28 @@
import { ContentElement } from '../Abstract/ContentElement';
import { FactElement } from './Fact';
export class FactSetElement extends ContentElement {
constructor(json, parent) {
super(json, parent);
this.facts = [];
if (this.isValid) {
this.facts = [];
if (json.facts) {
json.facts.forEach((item) => {
let fact = new FactElement(item, this);
if (fact && fact.isValid) {
this.facts.push(fact);
}
});
}
}
}
get children() {
if (this.facts) {
return this.facts;
}
return [];
}
get requiredProperties() {
return ['type', 'facts'];
}
}

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

@ -0,0 +1,29 @@
import { ContentElement } from '../Abstract/ContentElement';
import { ImageElement } from '../CardElements/Image';
export class ImageSetElement extends ContentElement {
constructor(json, parent) {
super(json, parent);
this.images = [];
if (this.isValid) {
this.imageSize = json.imageSize;
this.images = [];
if (json.images) {
json.images.forEach((item) => {
let image = new ImageElement(item, this);
if (image && image.isValid) {
this.images.push(image);
}
});
}
}
}
get children() {
if (this.images) {
return this.images;
}
return [];
}
get requiredProperties() {
return ['type', 'images'];
}
}

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

@ -0,0 +1,39 @@
import { ActionType } from '../Abstract/ActionElement';
import { OpenUrlActionElement } from '../Actions/OpenUrlAction';
import { ShowCardActionElement } from '../Actions/ShowCardAction';
import { SubmitActionElement } from '../Actions/SubmitAction';
export class ActionFactory {
static create(json, parent) {
if (!json) {
return undefined;
}
let action;
switch (json.type) {
case ActionType.OpenUrl:
action = new OpenUrlActionElement(json, parent);
break;
case ActionType.Submit:
action = new SubmitActionElement(json, parent);
break;
case ActionType.ShowCard:
action = new ShowCardActionElement(json, parent);
break;
default:
action = undefined;
break;
}
return action;
}
static createSet(json, parent) {
let actionSet = [];
if (json && json.length > 0) {
json.forEach((item) => {
let action = ActionFactory.create(item, parent);
if (action && action.isValid) {
actionSet.push(action);
}
});
}
return actionSet;
}
}

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

@ -0,0 +1,81 @@
import { ContentElementType } from '../Abstract/ContentElement';
import { InputElementType } from '../Abstract/InputElement';
import { FormElementType } from '../Abstract/ScopeElement';
import { ImageElement } from '../CardElements/Image';
import { TextBlockElement } from '../CardElements/TextBlock';
import { CardElement } from '../Cards/Card';
import { ColumnElement } from '../Containers/Column';
import { ColumnSetElement } from '../Containers/ColumnSet';
import { ContainerElement } from '../Containers/Container';
import { FactSetElement } from '../Containers/FactSet';
import { ImageSetElement } from '../Containers/ImageSet';
import { DateInputElement } from '../Inputs/DateInput';
import { NumberInputElement } from '../Inputs/NumberInput';
import { PeoplePickerElement } from '../Inputs/PeoplePicker';
import { TextInputElement } from '../Inputs/TextInput';
import { TimeInputElement } from '../Inputs/TimeInput';
export class ContentElementFactory {
static create(json, parent) {
if (!json) {
return null;
}
let cardElement;
switch (json.type) {
case ContentElementType.Image:
cardElement = new ImageElement(json, parent);
break;
case ContentElementType.TextBlock:
cardElement = new TextBlockElement(json, parent);
break;
case FormElementType.Column:
cardElement = new ColumnElement(json, parent);
break;
case FormElementType.ColumnSet:
cardElement = new ColumnSetElement(json, parent);
break;
case FormElementType.Container:
cardElement = new ContainerElement(json, parent);
break;
case ContentElementType.FactSet:
cardElement = new FactSetElement(json, parent);
break;
case ContentElementType.ImageSet:
cardElement = new ImageSetElement(json, parent);
break;
case InputElementType.TextInput:
cardElement = new TextInputElement(json, parent);
break;
case InputElementType.DateInput:
cardElement = new DateInputElement(json, parent);
break;
case InputElementType.TimeInput:
cardElement = new TimeInputElement(json, parent);
break;
case InputElementType.NumberInput:
cardElement = new NumberInputElement(json, parent);
break;
case InputElementType.PeoplePicker:
cardElement = new PeoplePickerElement(json, parent);
break;
case ContentElementType.AdaptiveCard:
cardElement = new CardElement(json, parent);
break;
default:
cardElement = null;
break;
}
return cardElement;
}
static createSet(json, parent) {
let cardElementSet = [];
if (json && json.length > 0) {
json.forEach((item) => {
let cardElement = ContentElementFactory.create(item, parent);
if (cardElement && cardElement.isValid) {
cardElementSet.push(cardElement);
}
});
}
return cardElementSet;
}
}

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

@ -0,0 +1,7 @@
import { ValueElement } from '../Abstract/ValueElement';
export class ChoiceInputElement extends ValueElement {
constructor(json, parent) {
super(json, parent);
this.children = [];
}
}

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

@ -0,0 +1,33 @@
import { InputElement } from '../Abstract/InputElement';
import { ChoiceInputElement } from './ChoiceInput';
export class ChoiceInputSetElement extends InputElement {
constructor(json, parent) {
super(json, parent);
this.choices = [];
if (this.isValid) {
this.isMultiSelect = json.isMultiSelect || false;
this.style = json.style;
this.choices = [];
if (json.choices) {
json.choices.forEach((item) => {
let inputChoice = new ChoiceInputElement(item, this);
if (inputChoice && inputChoice.isValid) {
this.choices.push(inputChoice);
}
});
}
}
}
get children() {
if (this.choices) {
return this.choices;
}
return [];
}
validate(input) {
return true;
}
get requiredProperties() {
return ['type', 'id', 'choices'];
}
}

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

@ -0,0 +1,26 @@
import { NumberUtils } from '../../Utils/NumberUtils';
import { TimeUtils } from '../../Utils/TimeUtils';
import { InputElement } from '../Abstract/InputElement';
export class DateInputElement extends InputElement {
constructor(json, parent) {
super(json, parent);
this.children = [];
if (this.isValid) {
this.max = json.max;
this.min = json.min;
this.placeholder = json.placeholder;
}
}
validate(input) {
if (input && input.length !== 0) {
let minTime = TimeUtils.extractDate(this.min);
let maxTime = TimeUtils.extractDate(this.max);
let time = TimeUtils.extractDate(input);
return NumberUtils.isInRange(time, minTime, maxTime);
}
return true;
}
get requiredProperties() {
return ['type', 'id'];
}
}

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

@ -0,0 +1,24 @@
import { NumberUtils } from '../../Utils/NumberUtils';
import { InputElement } from '../Abstract/InputElement';
export class NumberInputElement extends InputElement {
constructor(json, parent) {
super(json, parent);
this.children = [];
if (this.isValid) {
this.max = json.max;
this.min = json.min;
this.placeholder = json.placeholder;
}
}
validate(input) {
if (input && input.length !== 0) {
if (NumberUtils.isNumberStrict(input)) {
return NumberUtils.isInRange(Number(input), this.min, this.max);
}
}
return true;
}
get requiredProperties() {
return ['type', 'id'];
}
}

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

@ -0,0 +1,29 @@
import { InputElement } from '../Abstract/InputElement';
import { CallbackAction } from '../Internal/CallbackAction';
export class PeoplePickerElement extends InputElement {
constructor(json, parent) {
super(json, parent);
if (this.isValid) {
this.callback = new CallbackAction(json.callback, this);
this.placeholder = json.placeholder;
}
}
get action() {
return this.callback;
}
get inputFields() {
return ['selected_people'];
}
getBackgroundImageUrl() {
return undefined;
}
validate(input) {
return true;
}
validateScope() {
return true;
}
get requiredProperties() {
return ['type'];
}
}

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

@ -0,0 +1,24 @@
import { InputElement } from '../Abstract/InputElement';
export class TextInputElement extends InputElement {
constructor(json, parent) {
super(json, parent);
this.children = [];
if (this.isValid) {
this.isMultiline = json.isMultiline || false;
this.maxLength = json.maxLength;
this.placeholder = json.placeholder;
this.style = json.style;
}
}
validate(input) {
if (this.maxLength) {
if (input !== undefined && input.length > this.maxLength) {
return false;
}
}
return true;
}
get requiredProperties() {
return ['type', 'id'];
}
}

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

@ -0,0 +1,26 @@
import { NumberUtils } from '../../Utils/NumberUtils';
import { TimeUtils } from '../../Utils/TimeUtils';
import { InputElement } from '../Abstract/InputElement';
export class TimeInputElement extends InputElement {
constructor(json, parent) {
super(json, parent);
this.children = [];
if (this.isValid) {
this.max = json.max;
this.min = json.min;
this.placeholder = json.placeholder;
}
}
validate(input) {
if (input && input.length !== 0) {
let minTime = TimeUtils.extractTime(this.min);
let maxTime = TimeUtils.extractTime(this.max);
let time = TimeUtils.extractTime(input);
return NumberUtils.isInRange(time, minTime, maxTime);
}
return true;
}
get requiredProperties() {
return ['type', 'id'];
}
}

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

@ -0,0 +1,18 @@
import { InputElement } from '../Abstract/InputElement';
export class ToggleInputElement extends InputElement {
constructor(json, parent) {
super(json, parent);
this.children = [];
if (this.isValid) {
this.title = json.title;
this.valueOff = json.valueOff;
this.valueOn = json.valueOn;
}
}
validate(input) {
return true;
}
get requiredProperties() {
return ['type', 'id', 'title'];
}
}

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

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

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

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

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

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

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

@ -0,0 +1,20 @@
import { ActionElement } from '../Abstract/ActionElement';
export class CallbackAction extends ActionElement {
constructor(json, parent) {
super(json, parent);
this.children = [];
if (this.isValid) {
this.url = json.url;
this.parameters = json.parameters;
}
}
get scope() {
if (this.parent) {
return this.parent;
}
return undefined;
}
get requiredProperties() {
return ['type', 'url'];
}
}

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

@ -0,0 +1,9 @@
export class Guid {
static newGuid() {
let id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
return id;
}
}

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

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

@ -0,0 +1,210 @@
import { StyleSheet } from 'react-native';
import { HostContext } from '../Contexts/HostContext';
export class StyleManager {
static getSpacing(spacing) {
if (spacing === 'none') {
return 0;
}
let config = HostContext.getInstance().getConfig();
let spacingConfig = config.spacing[spacing];
if (!spacingConfig) {
return config.spacing.default;
}
return spacingConfig;
}
static getFontSize(size) {
let config = HostContext.getInstance().getConfig();
let fontSize = config.fontSize[size];
if (!fontSize) {
return config.fontSize.default;
}
return fontSize;
}
static getFontWeight(weight) {
let config = HostContext.getInstance().getConfig();
let fontWeight = config.fontWeight[weight];
if (!fontWeight) {
return config.fontWeight.default;
}
return fontWeight;
}
static getTextAlign(align) {
if (align === 'left' || align === 'center' || align === 'right') {
return align;
}
return 'left';
}
static getHorizontalAlign(align) {
switch (align) {
case 'left':
return 'flex-start';
case 'right':
return 'flex-end';
case 'center':
return 'center';
default:
return 'center';
}
}
static getWrap(wrap) {
if (wrap) {
return 'wrap';
}
return 'nowrap';
}
static getImageSize(size) {
let config = HostContext.getInstance().getConfig();
if (size === undefined) {
return 'auto';
}
if (size === 'small' || size === 'medium' || size === 'large') {
return config.imageSize[size];
}
return size;
}
static getColor(color, theme, isSubtle) {
let config = HostContext.getInstance().getConfig();
let themeConfig = config.container[theme];
if (!themeConfig) {
themeConfig = config.container.default;
}
let colorConfig = themeConfig.foreground[color];
if (!colorConfig) {
colorConfig = themeConfig.foreground.default;
}
if (isSubtle) {
return colorConfig.subtle;
}
else {
return colorConfig.default;
}
}
static getBackgroundColor(theme) {
let config = HostContext.getInstance().getConfig();
let themeConfig = config.container[theme];
if (!themeConfig) {
return config.container.default.background;
}
return themeConfig.background;
}
static getFactTitleColor(theme) {
let config = HostContext.getInstance().getConfig();
let color = config.factSet.title.color;
let isSubtle = config.factSet.title.isSubtle;
return StyleManager.getColor(color, theme, isSubtle);
}
static getFactValueColor(theme) {
let config = HostContext.getInstance().getConfig();
let color = config.factSet.value.color;
let isSubtle = config.factSet.value.isSubtle;
return StyleManager.getColor(color, theme, isSubtle);
}
static getInputColor(theme) {
let config = HostContext.getInstance().getConfig();
let themeConfig = config.input[theme];
if (!themeConfig) {
themeConfig = config.input.default;
}
return themeConfig.color;
}
static getInputFocusColor(theme) {
let config = HostContext.getInstance().getConfig();
let themeConfig = config.input[theme];
if (!themeConfig) {
themeConfig = config.input.default;
}
return themeConfig.focusColor;
}
static getInputBackgroundColor(theme) {
let config = HostContext.getInstance().getConfig();
let themeConfig = config.input[theme];
if (!themeConfig) {
themeConfig = config.input.default;
}
return themeConfig.backgroundColor;
}
static getInputFocusBackgroundColor(theme) {
let config = HostContext.getInstance().getConfig();
let themeConfig = config.input[theme];
if (!themeConfig) {
themeConfig = config.input.default;
}
return themeConfig.focusBackgroundColor;
}
static getInputBorderColor(theme) {
let config = HostContext.getInstance().getConfig();
let themeConfig = config.input[theme];
if (!themeConfig) {
themeConfig = config.input.default;
}
return themeConfig.borderColor;
}
static getInputFocusBorderColor(theme) {
let config = HostContext.getInstance().getConfig();
let themeConfig = config.input[theme];
if (!themeConfig) {
themeConfig = config.input.default;
}
return themeConfig.focusBorderColor;
}
static get inSetImageSize() {
return HostContext.getInstance().getConfig().imageSet.imageSize;
}
static get inSetImageMaxHeight() {
return HostContext.getInstance().getConfig().imageSet.maxImageHeight;
}
static get factSetSpacing() {
return HostContext.getInstance().getConfig().factSet.margin;
}
static get factTitleFontSize() {
return StyleManager.getFontSize(HostContext.getInstance().getConfig().factSet.title.size);
}
static get factTitleFontWeight() {
return StyleManager.getFontWeight(HostContext.getInstance().getConfig().factSet.title.weight);
}
static get factTitleWrap() {
return StyleManager.getWrap(HostContext.getInstance().getConfig().factSet.title.wrap);
}
static get factValueFontSize() {
return StyleManager.getFontSize(HostContext.getInstance().getConfig().factSet.value.size);
}
static get factValueFontWeight() {
return StyleManager.getFontWeight(HostContext.getInstance().getConfig().factSet.value.weight);
}
static get factValueWrap() {
return StyleManager.getWrap(HostContext.getInstance().getConfig().factSet.value.wrap);
}
static get fontFamily() {
return HostContext.getInstance().getConfig().fontFamily;
}
static get separatorThickness() {
return HostContext.getInstance().getConfig().separator.thickness * StyleSheet.hairlineWidth;
}
static get separatorColor() {
return HostContext.getInstance().getConfig().separator.color;
}
static get separatorSpacing() {
return HostContext.getInstance().getConfig().separator.spacing;
}
static get maxActions() {
return HostContext.getInstance().getConfig().action.capacity;
}
static get actionSetSpacing() {
return StyleManager.getSpacing(HostContext.getInstance().getConfig().action.actionSetSpacing);
}
static get actionSpacing() {
return HostContext.getInstance().getConfig().action.actionSpacing;
}
static get actionDirection() {
return HostContext.getInstance().getConfig().action.direction;
}
static get actionAlignment() {
return HostContext.getInstance().getConfig().action.align;
}
static get subCardSpacing() {
return HostContext.getInstance().getConfig().action.showCard.margin;
}
static get subCardTheme() {
return HostContext.getInstance().getConfig().action.showCard.style;
}
}

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

@ -0,0 +1,11 @@
export class ConsoleUtils {
static log(type, message) {
console.log(`${type}: ${message}`);
}
static warning(type, message) {
console.warn(`${type}: ${message}`);
}
static error(type, message) {
console.error(`${type}: ${message}`);
}
}

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

@ -0,0 +1,10 @@
export class ElementUtils {
static isInput(type) {
return ElementUtils.inputTypes.indexOf(type) >= 0;
}
static isValue(type) {
return ElementUtils.valueTypes.indexOf(type) >= 0;
}
}
ElementUtils.inputTypes = ['Input.Text', 'Input.Number', 'Input.Date', 'Input.Time', 'Input.Toggle', 'Input.ChoiceSet'];
ElementUtils.valueTypes = ['Fact', 'Input.Choice'];

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

@ -0,0 +1,35 @@
export class EnumUtils {
static getEnumValueOrDefault(targetEnum, name, defaultValue) {
if (!name) {
return defaultValue;
}
for (const key in targetEnum) {
if (targetEnum.hasOwnProperty(key)) {
let isValueProperty = parseInt(key, 10) >= 0;
if (isValueProperty) {
let value = targetEnum[key];
if (value && typeof value === 'string') {
if (value.toLowerCase() === name.toLowerCase()) {
return parseInt(key, 10);
}
}
}
}
}
return defaultValue;
}
static getStringEnumValueOrDefault(targetEnum, name, defaultValue) {
if (!name) {
return defaultValue;
}
for (const key in targetEnum) {
if (targetEnum.hasOwnProperty(key)) {
let value = targetEnum[key];
if (value.toLowerCase() === name.toLowerCase()) {
return value;
}
}
}
return defaultValue;
}
}

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

@ -0,0 +1,80 @@
import { Image } from 'react-native';
import { StyleManager } from '../Styles/StyleManager';
import { UrlUtils } from './UrlUtils';
export class ImageUtils {
static fetchSize(url, size, contract, onSize, onError) {
if (UrlUtils.isSvgXml(url) || UrlUtils.isDeepLink(url)) {
ImageUtils.fetchSizeFromConfig(size, contract, onSize);
}
else {
ImageUtils.fetchSizeFromImage(url, size, contract, onSize, onError);
}
}
static fetchSizeFromConfig(size, contract, onSize) {
let width = StyleManager.getImageSize(size);
if (typeof width !== 'number') {
width = StyleManager.getImageSize('large');
}
if (onSize) {
onSize(ImageUtils.fitContract({ width: width, height: width }, contract));
}
}
static fetchSizeFromImage(url, size, contract, onSize, onError) {
Image.getSize(url, (width, height) => {
if (width && height && width > 0 && height > 0) {
let basis = StyleManager.getImageSize(size);
let result = {
width: width,
height: height,
};
if (typeof basis === 'number') {
let ratio = result.width / result.height;
if (ratio > 1) {
result.height = basis;
result.width = basis * ratio;
}
else {
result.width = basis;
result.height = basis / ratio;
}
}
if (onSize) {
onSize(ImageUtils.fitContract(result, contract));
}
}
}, onError);
}
static fitContract(size, contract) {
if (contract) {
if (size && size.width && size.height && size.width > 0 && size.height > 0) {
let ratio = size.width / size.height;
let result = {
width: size.width,
height: size.height,
};
if (contract.height && result.height > contract.height) {
result.height = contract.height;
result.width = result.height * ratio;
}
if (contract.width && result.width > contract.width) {
result.width = contract.width;
result.height = result.width / ratio;
}
return result;
}
return contract;
}
else {
if (size && size.width && size.height && size.width > 0 && size.height > 0) {
return size;
}
else {
let width = StyleManager.getImageSize('large');
return {
width: width,
height: width,
};
}
}
}
}

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

@ -0,0 +1,33 @@
export class JsonUtils {
static isValidateJson(json, requiredProperties) {
if (!json) {
return {
isValid: false,
message: 'data not found',
};
}
if (requiredProperties) {
return requiredProperties.reduce((prev, current) => {
if (prev.isValid) {
prev.isValid = JsonUtils.isValidValue(json[current]);
if (!prev.isValid) {
if (current === 'title') {
prev.isValid = true;
}
prev.message = `${current} is required`;
}
}
return prev;
}, { isValid: true, message: '', });
}
return {
isValid: true,
message: ''
};
}
static isValidValue(value) {
return !(value === undefined ||
value === null ||
(typeof value === 'string' && !value && value !== ''));
}
}

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

@ -0,0 +1,23 @@
export class NumberUtils {
static isInRange(value, min, max) {
if (value !== undefined && min !== undefined && max !== undefined) {
return (value >= min && value <= max);
}
if (value !== undefined && min !== undefined) {
return (value >= min);
}
if (value !== undefined && max !== undefined) {
return (value <= max);
}
return true;
}
static isNumberStrict(value) {
return /^(\+|-)?\d+($|\.\d+$)/.test(value);
}
static isNumber(value) {
return /^(\+|-)?\d+($|\.\d*$)/.test(value);
}
static isSymbol(value) {
return /^(\+|-)?$/.test(value);
}
}

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

@ -0,0 +1,18 @@
export class StringUtils {
static prettifyString(valueString, fix, length, position) {
if (valueString && valueString.length < length && fix && fix.length > 0) {
let result = valueString;
let count = length - valueString.length;
for (let i = 0; i < count; i++) {
if (position === 'pre') {
result = fix[0] + result;
}
else {
result = result + fix[0];
}
}
return result;
}
return valueString;
}
}

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

@ -0,0 +1,8 @@
export class StyleUtils {
static isFlexWidth(flex) {
return flex && flex > 0;
}
static isFixedSize(width, height) {
return width && width > 0 && height && height > 0;
}
}

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

@ -0,0 +1,41 @@
import { StringUtils } from './StringUtils';
export class TimeUtils {
static isDate(value) {
return /^\d{4}(\-\d{2}){2}$/.test(value);
}
static isTime(value) {
return /^\d{2}\:\d{2}$/.test(value);
}
static extractDate(value) {
if (TimeUtils.isDate(value)) {
let parts = value.split('-');
return new Date(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]));
}
return undefined;
}
static extractTime(value) {
if (TimeUtils.isTime(value)) {
let parts = value.split(':');
return new Date(2020, 0, 1, Number(parts[0]), Number(parts[1]));
}
return undefined;
}
static getDateString(date) {
if (date) {
return (`${StringUtils.prettifyString(date.getFullYear().toString(), '0', 2, 'pre')}-` +
`${StringUtils.prettifyString((date.getMonth() + 1).toString(), '0', 2, 'pre')}-` +
`${StringUtils.prettifyString(date.getDate().toString(), '0', 2, 'pre')}`);
}
return undefined;
}
static getTimeString(date) {
if (date) {
return TimeUtils.composeTimeString(date.getHours(), date.getMinutes());
}
return undefined;
}
static composeTimeString(hour, minute) {
return (`${StringUtils.prettifyString(hour.toString(), '0', 2, 'pre')}:` +
`${StringUtils.prettifyString(minute.toString(), '0', 2, 'pre')}`);
}
}

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

@ -0,0 +1,14 @@
export class UrlUtils {
static isRemoteUrl(url) {
return (url && /^(((http)s*)|(ftp)):/.test(url));
}
static isEncodedData(url) {
return (url && /^data:/.test(url));
}
static isSvgXml(url) {
return (url && /(^data:image\/svg\+xml)|(\.svg$)/.test(url));
}
static isDeepLink(url) {
return !UrlUtils.isRemoteUrl(url) && !UrlUtils.isEncodedData(url);
}
}

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

@ -0,0 +1,50 @@
import * as React from 'react';
import { Button } from '../../Components/Inputs/Button';
import { ActionContext } from '../../Contexts/ActionContext';
import { StyleManager } from '../../Styles/StyleManager';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
export class ActionView extends React.Component {
constructor() {
super(...arguments);
this.onPress = () => {
let callback = ActionContext.getGlobalInstance().getActionEventHandler(this.props.element);
if (callback) {
if (this.props.actionHooks) {
callback(...this.props.actionHooks);
}
else {
callback();
}
}
};
}
render() {
const { element, theme } = this.props;
if (!element || !element.isValid) {
return DebugOutputFactory.createDebugOutputBanner(element.type + '>>' + element.title + ' is not valid', theme, 'error');
}
return (React.createElement(Button, { flex: 1, title: this.title, color: StyleManager.getColor('accent', theme, false), fontSize: StyleManager.getFontSize('default'), fontWeight: StyleManager.getFontWeight('bolder'), backgroundColor: StyleManager.getBackgroundColor(theme), textHorizontalAlign: 'center', textVerticalAlign: 'center', paddingTop: 6, paddingBottom: 6, paddingLeft: 16, paddingRight: 16, onPress: this.onPress, marginTop: StyleManager.actionDirection === 'vertically' ? this.spacing : 0, marginLeft: StyleManager.actionDirection === 'horizontal' ? this.spacing : 0, style: {
borderLeftWidth: this.leftBorderWidth,
borderLeftColor: StyleManager.separatorColor,
} }));
}
get leftBorderWidth() {
if (this.props.index !== undefined && this.props.index > 0) {
return 1;
}
return 0;
}
get spacing() {
if (this.props.index !== undefined && this.props.index > 0) {
return StyleManager.actionSpacing;
}
return 0;
}
get title() {
const { element } = this.props;
if (!element || !element.isValid) {
return '';
}
return this.props.element.title ? this.props.element.title.toLocaleUpperCase() : '';
}
}

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

@ -0,0 +1,80 @@
import * as React from 'react';
import { ImageBlock } from '../../Components/Basic/ImageBlock';
import { ActionContext } from '../../Contexts/ActionContext';
import { StyleManager } from '../../Styles/StyleManager';
import { ImageUtils } from '../../Utils/ImageUtils';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
export class ImageView extends React.Component {
constructor(props) {
super(props);
this.onPress = () => {
let callback = ActionContext.getGlobalInstance().getActionEventHandler(this.props.element.selectAction);
if (callback) {
callback();
}
};
this.onLayout = (event) => {
let ratio = this.state.width > 0 && this.state.height > 0 ? this.state.width / this.state.height : 1;
let width = event.nativeEvent.layout.width;
if (width !== 0 && width !== this.state.width) {
this.setState({
width: width,
height: width / ratio
});
}
};
this.onImageSizeError = (error) => {
console.log(error);
this.setState({
loaded: false,
});
};
this.onImageSize = (size) => {
if (size) {
this.setState({
loaded: true,
width: size.width,
height: size.height,
});
}
};
this.state = {
loaded: false,
width: 0,
height: 0,
};
}
componentDidMount() {
const { element, size, maxWidth, maxHeight } = this.props;
if (element && element.isValid) {
ImageUtils.fetchSize(element.url, size || element.size, { width: maxWidth, height: maxHeight }, this.onImageSize, this.onImageSizeError);
}
}
render() {
const { element, spacing, theme } = this.props;
if (!element || !element.isValid) {
return DebugOutputFactory.createDebugOutputBanner(element.type + '>>' + element.url + ' is not valid', theme, 'error');
}
if (this.state.loaded) {
return (React.createElement(ImageBlock, { url: element.url, alt: element.altText, flex: this.flex, alignSelf: StyleManager.getHorizontalAlign(element.horizontalAlignment), width: this.state.width, height: this.state.height, onPress: element.selectAction ? this.onPress : undefined, onLayout: this.onLayout, marginTop: this.spacing, marginLeft: spacing, mode: element.style === 'person' ? 'avatar' : 'default' }));
}
return null;
}
get spacing() {
if (this.props.index !== undefined && this.props.index > 0) {
return StyleManager.getSpacing(this.props.element.spacing);
}
return 0;
}
get flex() {
const { element, size } = this.props;
if (!element || !element.isValid) {
return undefined;
}
let finalSize = StyleManager.getImageSize(size || element.size);
if (finalSize === 'stretch') {
return 1;
}
return 0;
}
}

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

@ -0,0 +1,27 @@
import * as React from 'react';
import { Text } from 'react-native';
import { StyleManager } from '../../Styles/StyleManager';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
export class TextBlockView extends React.Component {
render() {
const { element, theme } = this.props;
if (!element || !element.isValid) {
return DebugOutputFactory.createDebugOutputBanner(element.type + '>>' + element.text + ' is not valid', theme, 'error');
}
return (React.createElement(Text, { accessible: true, style: {
color: StyleManager.getColor(element.color, this.props.theme, element.isSubtle),
fontSize: StyleManager.getFontSize(element.size),
fontWeight: StyleManager.getFontWeight(element.weight),
backgroundColor: 'transparent',
textAlign: StyleManager.getTextAlign(element.horizontalAlignment),
flexWrap: StyleManager.getWrap(element.wrap),
marginTop: this.spacing
}, numberOfLines: element.maxLines }, element.text));
}
get spacing() {
if (this.props.index !== undefined && this.props.index > 0) {
return StyleManager.getSpacing(this.props.element.spacing);
}
return 0;
}
}

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

@ -0,0 +1,89 @@
import * as React from 'react';
import { View } from 'react-native';
import { ButtonGroup } from '../../Components/Containers/ButtonGroup';
import { Card } from '../../Components/Containers/Card';
import { ActionContext } from '../../Contexts/ActionContext';
import { ActionType } from '../../Schema/Abstract/ActionElement';
import { StyleManager } from '../../Styles/StyleManager';
import { ActionFactory } from '../Factories/ActionFactory';
import { ContentFactory } from '../Factories/ContentFactory';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
export class AdaptiveCardView extends React.Component {
constructor(props) {
super(props);
this.showSubCard = (args) => {
console.log('Show Card');
if (args && args.action && args.action.type === ActionType.ShowCard) {
if (this.state.subCard !== args.action.card) {
this.setState({
subCard: args.action.card
});
}
else {
this.setState({
subCard: undefined
});
}
}
return args;
};
this.state = {};
this.actionContext = ActionContext.createInstance();
this.actionContext.registerHook({ func: this.showSubCard, name: 'showSubCard', actionType: ActionType.ShowCard });
}
render() {
const { element, theme } = this.props;
if (!element || !element.isValid) {
return DebugOutputFactory.createDebugOutputBanner(element.type + '>>' + element.type + ' is not valid', theme, 'error');
}
return (React.createElement(Card, { flex: 1, fit: 'container', backgroundImageUrl: element.getBackgroundImageUrl(), style: [
{
minHeight: 150,
}, this.props.style
] },
this.renderBody(),
this.renderSubCard(),
this.renderActionSet()));
}
renderBody() {
const { element } = this.props;
if (!element || !element.isValid || !element.body) {
return undefined;
}
return (React.createElement(View, { style: {
flexDirection: 'column',
alignSelf: 'stretch',
flex: 1
} }, this.props.element.body.map((contentElement, index) => ContentFactory.createView(contentElement, index, this.props.theme))));
}
renderActionSet() {
const { element } = this.props;
if (!element || !element.isValid || !element.actions || element.actions.length === 0) {
return undefined;
}
return (React.createElement(ButtonGroup, null, this.renderActions()));
}
renderActions() {
const { element, theme } = this.props;
if (!element || !element.isValid || !element.actions) {
return undefined;
}
let capacity = StyleManager.maxActions;
return element.actions.map((action, index) => {
if (index < capacity) {
return ActionFactory.createAction(action, index, theme, this.actionContext);
}
else {
return undefined;
}
});
}
renderSubCard() {
if (this.state.subCard) {
return (React.createElement(AdaptiveCardView, { index: 2, element: this.state.subCard, style: {
marginTop: StyleManager.subCardSpacing
}, theme: StyleManager.subCardTheme }));
}
return undefined;
}
}

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

@ -0,0 +1,114 @@
import * as React from 'react';
import { View } from 'react-native';
import { Touchable } from '../../Components/Basic/Touchable';
import { ActionContext } from '../../Contexts/ActionContext';
import { StyleManager } from '../../Styles/StyleManager';
import { ContentFactory } from '../Factories/ContentFactory';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
export class ColumnView extends React.Component {
constructor() {
super(...arguments);
this.renderTouchableBlock = (backgroundColor) => {
return (React.createElement(Touchable, { onPress: this.onPress, style: {
flex: this.flex,
flexDirection: 'column',
alignSelf: this.alignSelf,
justifyContent: this.justifyContent,
marginLeft: this.spacing,
backgroundColor: backgroundColor,
} }, this.renderContent()));
};
this.renderNonTouchableBlock = (backgroundColor) => {
return (React.createElement(View, { style: {
flex: this.flex,
flexDirection: 'column',
alignSelf: this.alignSelf,
justifyContent: this.justifyContent,
marginLeft: this.spacing,
backgroundColor: backgroundColor
} }, this.renderContent()));
};
this.renderContent = () => {
const { element } = this.props;
if (!element || !element.isValid) {
return undefined;
}
const background = element.getBackgroundImageUrl();
if (background) {
return ContentFactory.createBackgroundImageView(this.renderItems(), background);
}
return this.renderItems();
};
this.renderItems = () => {
const { element } = this.props;
if (!element || !element.isValid) {
return undefined;
}
if (element.items) {
return element.items.map((content, index) => ContentFactory.createView(content, index, element.style || this.props.theme));
}
return undefined;
};
this.onPress = () => {
let callback = ActionContext.getGlobalInstance().getActionEventHandler(this.props.element.selectAction);
if (callback) {
callback();
}
};
}
render() {
const { element, theme } = this.props;
if (!element || !element.isValid) {
return DebugOutputFactory.createDebugOutputBanner(element.type + '>>' + element.id + ' is not valid', theme, 'error');
}
let backgroundColor = StyleManager.getBackgroundColor(element.style);
if (element.selectAction) {
return this.renderTouchableBlock(backgroundColor);
}
else {
return this.renderNonTouchableBlock(backgroundColor);
}
}
get justifyContent() {
const { element } = this.props;
if (!element || !element.isValid) {
return 'flex-start';
}
switch (element.verticalContentAlignment) {
case 'top':
return 'flex-start';
case 'center':
return 'center';
case 'bottom':
return 'flex-end';
default:
return 'center';
}
}
get alignSelf() {
const { element } = this.props;
if (!element || !element.isValid) {
return 'flex-start';
}
if (element.height === 'stretch') {
return 'stretch';
}
return 'flex-start';
}
get flex() {
const { element } = this.props;
if (!element || !element.isValid || element.width === 'auto') {
return 0;
}
if (element.width === undefined || element.width === 'stretch') {
return 1;
}
return element.width;
}
get spacing() {
if (this.props.index !== undefined && this.props.index > 0) {
return StyleManager.getSpacing(this.props.element.spacing);
}
return 0;
}
}

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

@ -0,0 +1,71 @@
import * as React from 'react';
import { View } from 'react-native';
import { Touchable } from '../../Components/Basic/Touchable';
import { ActionContext } from '../../Contexts/ActionContext';
import { StyleManager } from '../../Styles/StyleManager';
import { ContentFactory } from '../Factories/ContentFactory';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
import { ColumnView } from './Column';
export class ColumnSetView extends React.Component {
constructor() {
super(...arguments);
this.renderTouchableBlock = () => {
return (React.createElement(Touchable, { onPress: this.onPress, style: {
flexDirection: 'row',
alignSelf: 'stretch',
justifyContent: 'flex-start',
marginTop: this.spacing,
} }, this.renderContent()));
};
this.renderNonTouchableBlock = () => {
return (React.createElement(View, { style: {
flexDirection: 'row',
alignSelf: 'stretch',
justifyContent: 'flex-start',
marginTop: this.spacing
} }, this.renderContent()));
};
this.renderContent = () => {
const { element } = this.props;
if (!element || !element.isValid) {
return undefined;
}
const background = element.getBackgroundImageUrl();
if (background) {
return ContentFactory.createBackgroundImageView(this.renderColumns(), background);
}
return this.renderColumns();
};
this.renderColumns = () => {
const { element } = this.props;
if (!element || !element.isValid || !element.columns || element.columns.length === 0) {
return undefined;
}
return element.columns.map((column, index) => (React.createElement(ColumnView, { key: index, index: index, element: column, theme: this.props.theme })));
};
this.onPress = () => {
let callback = ActionContext.getGlobalInstance().getActionEventHandler(this.props.element.selectAction);
if (callback) {
callback();
}
};
}
render() {
const { element, theme } = this.props;
if (!element || !element.isValid) {
return DebugOutputFactory.createDebugOutputBanner(element.type + '>>' + element.id + ' is not valid', theme, 'error');
}
if (element.selectAction) {
return this.renderTouchableBlock();
}
else {
return this.renderNonTouchableBlock();
}
}
get spacing() {
if (this.props.index !== undefined && this.props.index > 0) {
return StyleManager.getSpacing(this.props.element.spacing);
}
return 0;
}
}

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

@ -0,0 +1,102 @@
import * as React from 'react';
import { View } from 'react-native';
import { Touchable } from '../../Components/Basic/Touchable';
import { ActionContext } from '../../Contexts/ActionContext';
import { StyleManager } from '../../Styles/StyleManager';
import { ContentFactory } from '../Factories/ContentFactory';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
export class ContainerView extends React.Component {
constructor() {
super(...arguments);
this.renderTouchableBlock = (backgroundColor) => {
return (React.createElement(Touchable, { onPress: this.onPress, style: {
flex: this.flex,
alignSelf: 'stretch',
justifyContent: this.justifyContent,
marginTop: this.spacing,
backgroundColor: backgroundColor,
} }, this.renderContent()));
};
this.renderNonTouchableBlock = (backgroundColor) => {
return (React.createElement(View, { style: {
flex: this.flex,
alignSelf: 'stretch',
justifyContent: this.justifyContent,
marginTop: this.spacing,
backgroundColor: backgroundColor,
} }, this.renderContent()));
};
this.renderContent = () => {
const { element } = this.props;
if (!element || !element.isValid) {
return undefined;
}
const background = element.getBackgroundImageUrl();
if (background) {
return ContentFactory.createBackgroundImageView(this.renderItems(), background);
}
return this.renderItems();
};
this.renderItems = () => {
const { element } = this.props;
if (!element || !element.isValid) {
return undefined;
}
if (element.items) {
return element.items.map((content, index) => ContentFactory.createView(content, index, element.style || this.props.theme));
}
return undefined;
};
this.onPress = () => {
let callback = ActionContext.getGlobalInstance().getActionEventHandler(this.props.element.selectAction);
if (callback) {
callback();
}
};
}
render() {
const { element, theme } = this.props;
if (!element || !element.isValid) {
return DebugOutputFactory.createDebugOutputBanner(element.type + '>>' + element.id + ' is not valid', theme, 'error');
}
let backgroundColor = StyleManager.getBackgroundColor(element.style);
if (element.selectAction) {
return this.renderTouchableBlock(backgroundColor);
}
else {
return this.renderNonTouchableBlock(backgroundColor);
}
}
get justifyContent() {
const { element } = this.props;
if (!element || !element.isValid) {
return 'flex-start';
}
switch (element.verticalContentAlignment) {
case 'top':
return 'flex-start';
case 'center':
return 'center';
case 'bottom':
return 'flex-end';
default:
return 'center';
}
}
get flex() {
const { element } = this.props;
if (!element || !element.isValid) {
return 0;
}
if (element.height === 'stretch') {
return 1;
}
return 0;
}
get spacing() {
if (this.props.index !== undefined && this.props.index > 0) {
return StyleManager.getSpacing(this.props.element.spacing);
}
return 0;
}
}

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

@ -0,0 +1,30 @@
import * as React from 'react';
import { Text, View } from 'react-native';
import { StyleManager } from '../../Styles/StyleManager';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
export class FactView extends React.Component {
render() {
const { element, theme } = this.props;
if (!element || !element.isValid) {
return DebugOutputFactory.createDebugOutputBanner(element.type + '>>' + element.title + ' is not valid', theme, 'error');
}
return (React.createElement(View, { style: {
flexDirection: 'row',
alignSelf: 'stretch'
} },
React.createElement(Text, { accessible: true, style: {
color: StyleManager.getFactTitleColor(theme),
fontSize: StyleManager.factTitleFontSize,
fontWeight: StyleManager.factTitleFontWeight,
flexWrap: StyleManager.factTitleWrap,
marginRight: 16,
} }, element.title),
React.createElement(Text, { style: {
color: StyleManager.getFactValueColor(theme),
fontSize: StyleManager.factValueFontSize,
fontWeight: StyleManager.factValueFontWeight,
flexWrap: StyleManager.factValueWrap,
marginRight: 16,
} }, element.value)));
}
}

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

@ -0,0 +1,34 @@
import * as React from 'react';
import { View } from 'react-native';
import { StyleManager } from '../../Styles/StyleManager';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
import { FactView } from './Fact';
export class FactSetView extends React.Component {
constructor() {
super(...arguments);
this.renderFacts = () => {
const { element, theme } = this.props;
if (!element || !element.isValid || !element.facts || element.facts.length === 0) {
return undefined;
}
return element.facts.map((fact, index) => (React.createElement(FactView, { key: index, element: fact, theme: theme })));
};
}
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(View, { style: {
flexDirection: 'column',
marginTop: this.spacing,
alignSelf: 'stretch'
} }, this.renderFacts()));
}
get spacing() {
if (this.props.index !== undefined && this.props.index > 0) {
return StyleManager.getSpacing(this.props.element.spacing);
}
return 0;
}
}

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

@ -0,0 +1,53 @@
import * as React from 'react';
import { FlatList } from 'react-native';
import { StyleManager } from '../../Styles/StyleManager';
import { ImageView } from '../CardElements/Image';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
export class ImageSetView extends React.Component {
constructor() {
super(...arguments);
this.keyExtractor = (item, index) => {
return `url: ${item.url}, index: ${index}`;
};
this.renderImage = (info) => {
const { element, theme } = this.props;
if (!element || !element.isValid) {
return undefined;
}
return (React.createElement(ImageView, { key: info.index, index: 0, element: info.item, size: this.size, maxHeight: StyleManager.inSetImageMaxHeight, spacing: this.getImageSpacing(info.index), theme: theme }));
};
}
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(FlatList, { data: element.images, renderItem: this.renderImage, keyExtractor: this.keyExtractor, horizontal: true, style: {
marginTop: this.spacing
} }));
}
get size() {
const { element } = this.props;
if (!element || !element.isValid) {
return 'auto';
}
if (element.imageSize) {
return element.imageSize;
}
return StyleManager.inSetImageSize;
}
getImageSpacing(index) {
if (index > 0) {
return StyleManager.getSpacing('default');
}
else {
return 0;
}
}
get spacing() {
if (this.props.index !== undefined && this.props.index > 0) {
return StyleManager.getSpacing(this.props.element.spacing);
}
return 0;
}
}

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

@ -0,0 +1,16 @@
import React from 'react';
import { ActionView } from '../Actions/Action';
export class ActionFactory {
static createAction(element, index, theme, context) {
if (element) {
return (React.createElement(ActionView, { key: element.type + index, index: index, element: element, theme: theme, actionHooks: this.getHooks(context, element.type) }));
}
return null;
}
static getHooks(context, actionType) {
if (context) {
return context.getHooks(actionType);
}
return undefined;
}
}

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

@ -0,0 +1,70 @@
import * as React from 'react';
import { ImageBackground } from '../../Components/Basic/ImageBackground';
import { SeparateLine } from '../../Components/Basic/SeparateLine';
import { ContentElementType } from '../../Schema/Abstract/ContentElement';
import { ImageView } from '../CardElements/Image';
import { TextBlockView } from '../CardElements/TextBlock';
import { AdaptiveCardView } from '../Cards/AdaptiveCard';
import { ColumnSetView } from '../Containers/ColumnSet';
import { ContainerView } from '../Containers/Container';
import { FactSetView } from '../Containers/FactSet';
import { ImageSetView } from '../Containers/ImageSet';
import { DateInputView } from '../Inputs/DateInput';
import { NumberInputView } from '../Inputs/NumberInput';
import { TextInputView } from '../Inputs/TextInput';
import { TimeInputView } from '../Inputs/TimeInput';
export class ContentFactory {
static createView(element, index, theme) {
if (element) {
let elementView = ContentFactory.createElement(element, index, theme);
if (index > 0 && element.separator) {
return [
React.createElement(SeparateLine, { key: 'SeparateLine' + index }),
elementView
];
}
return [elementView];
}
return null;
}
static createBackgroundImageView(node, background) {
console.log(background);
if (background) {
return (React.createElement(ImageBackground, { url: background, flex: 1 }, node));
}
else {
return null;
}
}
static createElement(element, index, theme) {
if (element) {
switch (element.type) {
case ContentElementType.AdaptiveCard:
return (React.createElement(AdaptiveCardView, { key: 'TextBlockView' + index, element: element, index: index, theme: theme }));
case ContentElementType.TextBlock:
return (React.createElement(TextBlockView, { key: 'TextBlockView' + index, element: element, index: index, theme: theme }));
case ContentElementType.Image:
return (React.createElement(ImageView, { key: 'ImageView' + index, element: element, index: index, theme: theme }));
case ContentElementType.Container:
return (React.createElement(ContainerView, { key: 'ContainerView' + index, element: element, index: index, theme: theme }));
case ContentElementType.ColumnSet:
return (React.createElement(ColumnSetView, { key: 'ColumnSetView' + index, element: element, index: index, theme: theme }));
case ContentElementType.FactSet:
return (React.createElement(FactSetView, { key: 'FactSetView' + index, element: element, index: index, theme: theme }));
case ContentElementType.ImageSet:
return (React.createElement(ImageSetView, { key: 'ImageSetView' + index, element: element, index: index, theme: theme }));
case ContentElementType.TextInput:
return (React.createElement(TextInputView, { key: 'TextInputView' + index, element: element, index: index, theme: theme }));
case ContentElementType.NumberInput:
return (React.createElement(NumberInputView, { key: 'NumberInput' + index, element: element, index: index, theme: theme }));
case ContentElementType.DateInput:
return (React.createElement(DateInputView, { key: 'DateInputView' + index, element: element, index: index, theme: theme }));
case ContentElementType.TimeInput:
return (React.createElement(TimeInputView, { key: 'TimeInputView' + index, element: element, index: index, theme: theme }));
default:
return null;
}
}
return null;
}
}

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

@ -0,0 +1,11 @@
import * as React from 'react';
import { Banner } from '../../Components/Basic/Banner';
import { HostContext } from '../../Contexts/HostContext';
export class DebugOutputFactory {
static createDebugOutputBanner(info, theme, level) {
if (HostContext.getInstance().getConfig().mode === 'debug') {
return (React.createElement(Banner, { title: info, theme: theme, level: level }));
}
return undefined;
}
}

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

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

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

@ -0,0 +1,66 @@
import * as React from 'react';
import { InputBox } from '../../Components/Inputs/InputBox';
import { FormContext } from '../../Contexts/FormContext';
import { HostContext } from '../../Contexts/HostContext';
import { StyleManager } from '../../Styles/StyleManager';
import { NumberUtils } from '../../Utils/NumberUtils';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
export class NumberInputView extends React.Component {
constructor(props) {
super(props);
this.onBlur = () => {
console.log('NumberInputView onBlur');
let callback = HostContext.getInstance().getHandler('blur');
if (callback) {
callback();
}
};
this.onFocus = () => {
console.log('NumberInputView onFocus');
let callback = HostContext.getInstance().getHandler('focus');
if (callback) {
callback();
}
};
this.onValueChange = (value) => {
this.setState({
value: value
}, this.updateStore);
};
this.validateInput = (input) => {
if (this.props.element) {
return this.props.element.validate(input);
}
return true;
};
const { element } = this.props;
if (element && element.isValid) {
let defaultValue = element.value;
if (defaultValue === undefined) {
defaultValue = '';
}
if (NumberUtils.isNumber(element.value.toString())) {
this.state = {
value: element.value.toString(),
};
this.updateStore();
}
}
}
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(InputBox, { placeholder: element.placeholder, value: this.state.value, onValueChange: this.onValueChange, onBlur: this.onBlur, onFocus: this.onFocus, validateInput: this.validateInput, theme: theme, marginTop: this.spacing }));
}
updateStore() {
FormContext.getInstance().updateField(this.props.element.id, this.state.value, this.validateInput(this.state.value));
}
get spacing() {
if (this.props.index !== undefined && this.props.index > 0) {
return StyleManager.getSpacing(this.props.element.spacing);
}
return 0;
}
}

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

@ -0,0 +1,69 @@
import * as React from 'react';
import { InputBox } from '../../Components/Inputs/InputBox';
import { FormContext } from '../../Contexts/FormContext';
import { HostContext } from '../../Contexts/HostContext';
import { StyleManager } from '../../Styles/StyleManager';
import { DebugOutputFactory } from '../Factories/DebugOutputFactory';
export class TextInputView extends React.Component {
constructor(props) {
super(props);
this.onBlur = () => {
console.log('TextInputView onBlur');
let callback = HostContext.getInstance().getHandler('blur');
if (callback) {
callback();
}
};
this.onFocus = () => {
console.log('TextInputView onFocus');
let callback = HostContext.getInstance().getHandler('focus');
if (callback) {
callback();
}
};
this.onValueChange = (value) => {
this.setState({
value: value
}, this.updateStore);
};
this.validateInput = (input) => {
if (this.props.element) {
return this.props.element.validate(input);
}
return true;
};
const { element } = this.props;
if (element && element.isValid) {
let defaultValue = element.value;
if (defaultValue === undefined) {
defaultValue = '';
}
this.state = {
value: defaultValue
};
this.updateStore();
}
}
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(InputBox, { numberOfLines: this.numberOfLine, placeholder: element.placeholder, value: this.state.value, onValueChange: this.onValueChange, validateInput: this.validateInput, onFocus: this.onFocus, onBlur: this.onBlur, theme: theme, marginTop: this.spacing }));
}
updateStore() {
FormContext.getInstance().updateField(this.props.element.id, this.state.value, this.validateInput(this.state.value));
}
get numberOfLine() {
if (this.props.element.isMultiline) {
return 4;
}
return 1;
}
get spacing() {
if (this.props.index !== undefined && this.props.index > 0) {
return StyleManager.getSpacing(this.props.element.spacing);
}
return 0;
}
}

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

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

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

@ -0,0 +1,114 @@
import React from 'react';
import { Linking, View, } from 'react-native';
import { ActionContext } from '../Contexts/ActionContext';
import { FormContext } from '../Contexts/FormContext';
import { HostContext } from '../Contexts/HostContext';
import { ActionType } from '../Schema/Abstract/ActionElement';
import { CardElement } from '../Schema/Cards/Card';
import { StyleManager } from '../Styles/StyleManager';
import { AdaptiveCardView } from './Cards/AdaptiveCard';
export class CardRootView extends React.PureComponent {
constructor(props) {
super(props);
this.onOpenUrl = (args) => {
if (args) {
if (this.props.onOpenUrl) {
this.props.onOpenUrl(args.action.url);
}
else {
Linking.canOpenURL(args.action.url).then((supported) => {
if (supported) {
Linking.openURL(args.action.url);
}
});
}
}
};
this.onCallback = (args) => {
if (args) {
console.log('Form validate: ' + args.formValidate);
console.log(args.formData);
if (args.formValidate && this.props.onCallback) {
this.props.onCallback(args.action.url, args.formData).then((data) => {
if (args.onFinishCallback) {
args.onFinishCallback(data);
}
}).catch((error) => {
if (args.onErrorCallback) {
args.onErrorCallback(error);
}
});
}
}
};
this.onSubmit = (args) => {
if (args) {
console.log('Form validate: ' + args.formValidate);
console.log(args.formData);
if (args.formValidate && this.props.onSubmit) {
this.props.onSubmit(args.formData);
}
}
};
this.validateForm = (args) => {
if (args) {
args.formValidate = args.action.scope.validateScope();
}
return args;
};
this.validateCallbackParams = (args) => {
if (args) {
args.formValidate = args.action.scope.validateScope();
}
return args;
};
this.populateFormData = (args) => {
if (args && args.formValidate) {
args.formData = Object.assign({}, (args.action.data || {}), FormContext.getInstance().getFormData(args.action.scope.inputFields));
}
return args;
};
this.populateCallbackParamData = (args) => {
if (args && args.formValidate) {
args.formData = FormContext.getInstance().getCallbackParamData(args.action.parameters);
}
return args;
};
let hostContext = HostContext.getInstance();
console.log(StyleManager.getColor('accent', 'default', false));
hostContext.applyConfig(this.props.config);
hostContext.registerOpenUrlHandler(this.onOpenUrl);
hostContext.registerSubmitHandler(this.onSubmit);
hostContext.registerCallbackHandler(this.onCallback);
hostContext.registerFocusHandler(this.props.onFocus);
hostContext.registerBlurHandler(this.props.onBlur);
hostContext.registerErrorHandler(this.props.onError);
hostContext.registerInfoHandler(this.props.onInfo);
hostContext.registerWarningHandler(this.props.onWarning);
let actionContext = ActionContext.getGlobalInstance();
actionContext.registerHook({
func: this.validateForm,
name: 'validateForm',
actionType: ActionType.Submit
});
actionContext.registerHook({
func: this.validateCallbackParams,
name: 'validateCallbackParams',
actionType: ActionType.Callback
});
actionContext.registerHook({
func: this.populateFormData,
name: 'populateFormData',
actionType: ActionType.Submit
});
actionContext.registerHook({
func: this.populateCallbackParamData,
name: 'populateCallbackParamData',
actionType: ActionType.Callback
});
}
render() {
return (React.createElement(View, { style: { flex: 1 } },
React.createElement(AdaptiveCardView, { index: 0, element: new CardElement(this.props.adaptiveCard, undefined), theme: 'default', style: this.props.style })));
}
}

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

@ -0,0 +1,2 @@
import { CardRootView } from './Views/Root';
export default CardRootView;

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше