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:
Родитель
d049cb45c6
Коммит
70c1851b4d
18
gulpfile.js
18
gulpfile.js
|
@ -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 [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"presets": [
|
||||
"env",
|
||||
"react"
|
||||
],
|
||||
"plugins": [
|
||||
"transform-runtime",
|
||||
"transform-async-to-generator",
|
||||
"transform-object-rest-spread"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
# js vendor file with import/require
|
||||
src/assets/**
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
dist
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"parser": "babel-eslint",
|
||||
"plugins": [
|
||||
"react"
|
||||
],
|
||||
"rules": {
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:react/recommended"]
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
Двоичные данные
tool/src/assets/AdaptiveCards/Assets/Images/Placeholders/avatar_default.png
Normal file
Двоичные данные
tool/src/assets/AdaptiveCards/Assets/Images/Placeholders/avatar_default.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 13 KiB |
Двоичные данные
tool/src/assets/AdaptiveCards/Assets/Images/Placeholders/image_default.png
Normal file
Двоичные данные
tool/src/assets/AdaptiveCards/Assets/Images/Placeholders/image_default.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 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;
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче