prettier files

This commit is contained in:
liaoxuezhi 2019-09-09 13:48:08 +08:00
parent af7fe6a09b
commit 0b5e4cb2ea
162 changed files with 11158 additions and 11223 deletions

View File

@ -1,43 +1,42 @@
/**
* @file 便
* @author fex
*/
* @file 便
* @author fex
*/
import React from 'react';
import find = require('lodash/find');
import PropTypes from 'prop-types';
import hoistNonReactStatic = require('hoist-non-react-statics');
import qs from 'qs';
import { dataMapping } from './utils/tpl-builtin';
import { RendererEnv, RendererProps } from './factory';
import { noop, autobind } from './utils/helper';
import { RendererData, Action } from './types';
import {dataMapping} from './utils/tpl-builtin';
import {RendererEnv, RendererProps} from './factory';
import {noop, autobind} from './utils/helper';
import {RendererData, Action} from './types';
interface ScopedComponentType extends React.Component<RendererProps> {
doAction?: (action: Action, data: RendererData, throwErrors?: boolean) => void;
receive?: (values: RendererData, subPath?: string) => void;
reload?: (subPath?:string, query?:RendererData | null, ctx?: RendererData) => void;
reload?: (subPath?: string, query?: RendererData | null, ctx?: RendererData) => void;
}
export interface IScopedContext {
parent?: AlisIScopedContext;
registerComponent: (component:ScopedComponentType) => void;
unRegisterComponent: (component:ScopedComponentType) => void;
getComponentByName: (name:string) => ScopedComponentType | void;
registerComponent: (component: ScopedComponentType) => void;
unRegisterComponent: (component: ScopedComponentType) => void;
getComponentByName: (name: string) => ScopedComponentType | void;
getComponents: () => Array<ScopedComponentType>;
reload: (target:string, ctx: RendererData) => void;
send: (target:string, ctx: RendererData) => void;
};
reload: (target: string, ctx: RendererData) => void;
send: (target: string, ctx: RendererData) => void;
}
type AlisIScopedContext = IScopedContext;
export const ScopedContext = React.createContext(createScopedTools(''));
function createScopedTools(path?:string, parent?:AlisIScopedContext, env?: RendererEnv):IScopedContext {
const components:Array<ScopedComponentType> = [];
function createScopedTools(path?: string, parent?: AlisIScopedContext, env?: RendererEnv): IScopedContext {
const components: Array<ScopedComponentType> = [];
return {
parent,
registerComponent(component:ScopedComponentType) {
registerComponent(component: ScopedComponentType) {
// 不要把自己注册在自己的 Scoped 上,自己的 Scoped 是给孩子们注册的。
if (component.props.$path === path && parent) {
return parent.registerComponent(component);
@ -48,7 +47,7 @@ function createScopedTools(path?:string, parent?:AlisIScopedContext, env?: Rende
}
},
unRegisterComponent(component:ScopedComponentType) {
unRegisterComponent(component: ScopedComponentType) {
// 自己本身实际上注册在父级 Scoped 上。
if (component.props.$path === path && parent) {
return parent.unRegisterComponent(component);
@ -61,7 +60,7 @@ function createScopedTools(path?:string, parent?:AlisIScopedContext, env?: Rende
}
},
getComponentByName(name:string) {
getComponentByName(name: string) {
if (~name.indexOf('.')) {
const paths = name.split('.');
const len = paths.length;
@ -69,22 +68,25 @@ function createScopedTools(path?:string, parent?:AlisIScopedContext, env?: Rende
return paths.reduce((scope, name, idx) => {
if (scope && scope.getComponentByName) {
const result = scope.getComponentByName(name);
return result && idx < (len - 1) ? result.context : result;
return result && idx < len - 1 ? result.context : result;
}
return null;
}, this);
}
const resolved = find(components, component => component.props.name === name || component.props.id === name);
return resolved || parent && parent.getComponentByName(name);
const resolved = find(
components,
component => component.props.name === name || component.props.id === name
);
return resolved || (parent && parent.getComponentByName(name));
},
getComponents() {
return components.concat();
},
reload(target:string, ctx:any) {
reload(target: string, ctx: any) {
const scoped = this;
if (target === 'window') {
@ -114,7 +116,7 @@ function createScopedTools(path?:string, parent?:AlisIScopedContext, env?: Rende
});
},
send(receive:string, values:object) {
send(receive: string, values: object) {
const scoped = this;
let receives = typeof receive === 'string' ? receive.split(/\s*,\s*/) : receive;
@ -131,10 +133,10 @@ function createScopedTools(path?:string, parent?:AlisIScopedContext, env?: Rende
const component = scoped.getComponentByName(name);
if (component && component.receive) {
component.receive(values, subPath)
component.receive(values, subPath);
} else if (name === 'window' && env && env.updateLocation) {
const query = {
...location.search ? qs.parse(location.search.substring(1)) : {},
const query = {
...(location.search ? qs.parse(location.search.substring(1)) : {}),
...values
};
const link = location.pathname + '?' + qs.stringify(query);
@ -142,24 +144,32 @@ function createScopedTools(path?:string, parent?:AlisIScopedContext, env?: Rende
}
});
}
}
};
}
export function HocScoped<T extends {
$path?: string;
env: RendererEnv;
}>(ComposedComponent: React.ComponentType<T>):React.ComponentType<T & {
scopeRef?: (ref: any) => void
}> & {
export function HocScoped<
T extends {
$path?: string;
env: RendererEnv;
}
>(
ComposedComponent: React.ComponentType<T>
): React.ComponentType<
T & {
scopeRef?: (ref: any) => void;
}
> & {
ComposedComponent: React.ComponentType<T>;
} {
class ScopedComponent extends React.Component< T & {
scopeRef?: (ref: any) => void
}> {
class ScopedComponent extends React.Component<
T & {
scopeRef?: (ref: any) => void;
}
> {
static displayName = `Scoped(${ComposedComponent.displayName || ComposedComponent.name})`;
static contextType = ScopedContext;
static ComposedComponent = ComposedComponent;
ref:any;
ref: any;
getWrappedInstance() {
return this.ref;
@ -174,7 +184,7 @@ export function HocScoped<T extends {
this.ref = ref;
}
scoped = createScopedTools(this.props.$path, this.context, this.props.env)
scoped = createScopedTools(this.props.$path, this.context, this.props.env);
componentWillMount() {
const scopeRef = this.props.scopeRef;
@ -187,21 +197,18 @@ export function HocScoped<T extends {
}
render() {
const {
scopeRef,
...rest
} = this.props;
const {scopeRef, ...rest} = this.props;
return (
<ScopedContext.Provider value={this.scoped}>
<ComposedComponent {...rest as any /* todo */} ref={this.childRef} />
</ScopedContext.Provider>
)
);
}
}
hoistNonReactStatic(ScopedComponent, ComposedComponent);
return ScopedComponent;
};
}
export default HocScoped;

View File

@ -2,39 +2,20 @@
* @file api
* @author fex
*/
import {
SchemaNode,
Schema
} from './types';
import {
RendererProps,
RendererConfig,
addSchemaFilter
} from './factory';
import {
CheckboxControlRenderer
} from './renderers/Form/Checkbox';
import {
FormRenderer
} from './renderers/Form/index';
import {
FieldSetRenderer
} from './renderers/Form/FieldSet';
import {
TabsRenderer
} from './renderers/Form/Tabs';
import {
CardRenderer
} from './renderers/Card';
import {
ListItemRenderer
} from './renderers/List';
import { ButtonGroupControlRenderer } from './renderers/Form/ButtonGroup';
import { getLevelFromClassName } from './utils/helper';
import { ServiceRenderer } from './renderers/Form/Service';
import {SchemaNode, Schema} from './types';
import {RendererProps, RendererConfig, addSchemaFilter} from './factory';
import {CheckboxControlRenderer} from './renderers/Form/Checkbox';
import {FormRenderer} from './renderers/Form/index';
import {FieldSetRenderer} from './renderers/Form/FieldSet';
import {TabsRenderer} from './renderers/Form/Tabs';
import {CardRenderer} from './renderers/Card';
import {ListItemRenderer} from './renderers/List';
import {ButtonGroupControlRenderer} from './renderers/Form/ButtonGroup';
import {getLevelFromClassName} from './utils/helper';
import {ServiceRenderer} from './renderers/Form/Service';
// 兼容老的用法,老用法 label 用在 checkbox 的右侧内容,新用法用 option 来代替。
addSchemaFilter(function CheckboxPropsFilter(schema:Schema, renderer) {
addSchemaFilter(function CheckboxPropsFilter(schema: Schema, renderer) {
if (renderer.component !== CheckboxControlRenderer) {
return schema;
}
@ -50,12 +31,12 @@ addSchemaFilter(function CheckboxPropsFilter(schema:Schema, renderer) {
return schema;
});
function convertFieldSetTabs2Controls(schema:any) {
const toUpdate:any = {};
function convertFieldSetTabs2Controls(schema: any) {
const toUpdate: any = {};
let flag = false;
toUpdate.controls = Array.isArray(schema.controls) ? schema.controls.concat() : [];
toUpdate.controls = toUpdate.controls.map((control:any) => {
toUpdate.controls = toUpdate.controls.map((control: any) => {
if (Array.isArray(control)) {
let converted = convertFieldSetTabs2Controls({
type: 'group',
@ -67,23 +48,26 @@ function convertFieldSetTabs2Controls(schema:any) {
}
return converted;
}
}
return control;
});
schema.fieldSet && (Array.isArray(schema.fieldSet) ? schema.fieldSet : [schema.fieldSet]).forEach((fieldSet:any) => {
flag = true;
toUpdate.controls.push({
...convertFieldSetTabs2Controls(fieldSet),
type: 'fieldSet',
collapsable: schema.collapsable
schema.fieldSet &&
(Array.isArray(schema.fieldSet) ? schema.fieldSet : [schema.fieldSet]).forEach((fieldSet: any) => {
flag = true;
toUpdate.controls.push({
...convertFieldSetTabs2Controls(fieldSet),
type: 'fieldSet',
collapsable: schema.collapsable
});
});
});
schema.tabs && (flag = true) && toUpdate.controls.push({
type: 'tabs',
tabs: schema.tabs.map((tab:any) => convertFieldSetTabs2Controls(tab))
});
schema.tabs &&
(flag = true) &&
toUpdate.controls.push({
type: 'tabs',
tabs: schema.tabs.map((tab: any) => convertFieldSetTabs2Controls(tab))
});
if (flag) {
schema = {
@ -98,7 +82,7 @@ function convertFieldSetTabs2Controls(schema:any) {
// Form 中,把 fieldSet 和 tabs 转成 {type: 'fieldSet', controls: []}
// 同时把数组用法转成 {type: 'group', controls: []}
addSchemaFilter(function FormPropsFilter(schema:Schema, renderer) {
addSchemaFilter(function FormPropsFilter(schema: Schema, renderer) {
if (renderer.component !== FormRenderer) {
return schema;
}
@ -108,7 +92,7 @@ addSchemaFilter(function FormPropsFilter(schema:Schema, renderer) {
schema = convertFieldSetTabs2Controls(schema);
} else if (Array.isArray(schema.controls)) {
let flag = false;
let converted = schema.controls.map((control:any) => {
let converted = schema.controls.map((control: any) => {
if (Array.isArray(control)) {
let converted = convertFieldSetTabs2Controls({
type: 'group',
@ -119,7 +103,7 @@ addSchemaFilter(function FormPropsFilter(schema:Schema, renderer) {
flag = true;
}
return converted;
}
}
return control;
});
@ -135,14 +119,14 @@ addSchemaFilter(function FormPropsFilter(schema:Schema, renderer) {
});
// FieldSet 中把 controls 里面的数组用法转成 {type: 'group', controls: []}
addSchemaFilter(function FormPropsFilter(schema:Schema, renderer) {
addSchemaFilter(function FormPropsFilter(schema: Schema, renderer) {
if (renderer.component !== FieldSetRenderer) {
return schema;
}
if (Array.isArray(schema.controls)) {
let flag = false;
let converted = schema.controls.map((control:any) => {
let converted = schema.controls.map((control: any) => {
if (Array.isArray(control)) {
let converted = convertFieldSetTabs2Controls({
type: 'group',
@ -153,7 +137,7 @@ addSchemaFilter(function FormPropsFilter(schema:Schema, renderer) {
flag = true;
}
return converted;
}
}
return control;
});
@ -169,16 +153,16 @@ addSchemaFilter(function FormPropsFilter(schema:Schema, renderer) {
});
// Form 里面的 Tabs 中把 controls 里面的数组用法转成 {type: 'group', controls: []}
addSchemaFilter(function FormPropsFilter(schema:Schema, renderer) {
addSchemaFilter(function FormPropsFilter(schema: Schema, renderer) {
if (renderer.component !== TabsRenderer) {
return schema;
}
if (Array.isArray(schema.tabs)) {
let flag = false;
let converted = schema.tabs.map((tab:any) => {
let converted = schema.tabs.map((tab: any) => {
let flag2 = false;
let converted = (tab.controls || []).map((control:any) => {
let converted = (tab.controls || []).map((control: any) => {
if (Array.isArray(control)) {
let converted = convertFieldSetTabs2Controls({
type: 'group',
@ -189,7 +173,7 @@ addSchemaFilter(function FormPropsFilter(schema:Schema, renderer) {
flag2 = true;
}
return converted;
}
}
return control;
});
@ -215,9 +199,9 @@ addSchemaFilter(function FormPropsFilter(schema:Schema, renderer) {
return schema;
});
function convertArray2Hbox(arr:Array<any>):any {
function convertArray2Hbox(arr: Array<any>): any {
let flag = false;
let converted = arr.map((item:any) => {
let converted = arr.map((item: any) => {
if (Array.isArray(item)) {
flag = true;
return convertArray2Hbox(item);
@ -233,22 +217,21 @@ function convertArray2Hbox(arr:Array<any>):any {
type: 'hbox',
columns: converted
};
}
// CRUD/List 和 CRUD/Card 的 body 中的数组用法转成 hbox
addSchemaFilter(function(schema:Schema, renderer) {
addSchemaFilter(function(schema: Schema, renderer) {
if (renderer.component !== CardRenderer && renderer.component !== ListItemRenderer) {
return schema;
}
if (Array.isArray(schema.body)) {
let flag = false;
let converted = schema.body.map((item:any) => {
let converted = schema.body.map((item: any) => {
if (Array.isArray(item)) {
flag = true;
return convertArray2Hbox(item);
}
}
return item;
});
@ -264,7 +247,7 @@ addSchemaFilter(function(schema:Schema, renderer) {
});
// button group 的 btnClassName 和 btnActiveClassName 改成 btnLevel 和 btnActiveLevel 了
addSchemaFilter(function(scheam:Schema, renderer) {
addSchemaFilter(function(scheam: Schema, renderer) {
if (renderer.component !== ButtonGroupControlRenderer) {
return scheam;
}
@ -284,7 +267,7 @@ addSchemaFilter(function(scheam:Schema, renderer) {
});
// FieldSet className 定制样式方式改成 size 来配置
addSchemaFilter(function(scheam:Schema, renderer) {
addSchemaFilter(function(scheam: Schema, renderer) {
if (renderer.component !== FieldSetRenderer) {
return scheam;
}
@ -304,7 +287,7 @@ addSchemaFilter(function(scheam:Schema, renderer) {
});
// FieldSet className 定制样式方式改成 size 来配置
addSchemaFilter(function(scheam:Schema, renderer) {
addSchemaFilter(function(scheam: Schema, renderer) {
if (renderer.component !== ServiceRenderer) {
return scheam;
}
@ -318,4 +301,4 @@ addSchemaFilter(function(scheam:Schema, renderer) {
}
return scheam;
});
});

View File

@ -49,7 +49,7 @@ export class Alert extends React.Component<AlertProps, AlertState> {
show: false,
title: '',
content: '',
confirm: false,
confirm: false
};
constructor(props: AlertProps) {
super(props);
@ -66,7 +66,7 @@ export class Alert extends React.Component<AlertProps, AlertState> {
cancelText: '取消',
title: '系统消息',
alertBtnLevel: 'primary',
confirmBtnLevel: 'danger',
confirmBtnLevel: 'danger'
};
componentWillMount() {
@ -100,7 +100,7 @@ export class Alert extends React.Component<AlertProps, AlertState> {
this.setState(
{
show: false,
show: false
},
isConfirm ? () => this._resolve(confirmed) /*this._reject()*/ : undefined
);
@ -111,7 +111,7 @@ export class Alert extends React.Component<AlertProps, AlertState> {
title,
content,
show: true,
confirm: false,
confirm: false
});
}
@ -120,7 +120,7 @@ export class Alert extends React.Component<AlertProps, AlertState> {
title,
content,
show: true,
confirm: true,
confirm: true
});
return new Promise(resolve => {
@ -146,7 +146,7 @@ export class Alert extends React.Component<AlertProps, AlertState> {
confirmBtnLevel,
alertBtnLevel,
classnames: cx,
classPrefix,
classPrefix
} = this.props;
return (
<Modal show={this.state.show} onHide={this.handleCancel} container={container} ref={this.modalRef}>

View File

@ -23,7 +23,7 @@ export class Alert extends React.Component<AlertProps, AlertState> {
static defaultProps: Pick<AlertProps, 'level' | 'className' | 'showCloseButton'> = {
level: 'info',
className: '',
showCloseButton: false,
showCloseButton: false
};
static propsList: Array<string> = ['level', 'className', 'showCloseButton', 'onClose'];
@ -32,14 +32,14 @@ export class Alert extends React.Component<AlertProps, AlertState> {
this.handleClick = this.handleClick.bind(this);
this.state = {
show: true,
show: true
};
}
handleClick() {
this.setState(
{
show: false,
show: false
},
this.props.onClose
);

View File

@ -65,7 +65,7 @@ export class AsideNav extends React.Component<AsideNavProps, AsideNavState> {
<div className={cx('AsideNav-tooltip')}>{link.label}</div>
) : null,
isActive: (link: LinkItem) => link.open,
isOpen: (item: LinkItemProps) => (item.children ? item.children.some(item => item.open) : false),
isOpen: (item: LinkItemProps) => (item.children ? item.children.some(item => item.open) : false)
};
constructor(props: AsideNavProps) {
@ -84,12 +84,12 @@ export class AsideNav extends React.Component<AsideNavProps, AsideNavState> {
...item,
id: id++,
active: isActive,
open: isActive || isOpen(item as LinkItemProps),
open: isActive || isOpen(item as LinkItemProps)
};
},
1,
true
),
)
};
this.renderLink = this.renderLink.bind(this);
@ -113,12 +113,12 @@ export class AsideNav extends React.Component<AsideNavProps, AsideNavState> {
...item,
id: id++,
active: isActive,
open: isActive || isOpen(item as LinkItemProps),
open: isActive || isOpen(item as LinkItemProps)
};
},
1,
true
),
)
});
}
}
@ -134,11 +134,11 @@ export class AsideNav extends React.Component<AsideNavProps, AsideNavState> {
this.state.navigations,
(item: Navigation) => ({
...item,
open: link.id === item.id ? !item.open : item.open,
open: link.id === item.id ? !item.open : item.open
}),
1,
true
),
)
});
}
@ -152,7 +152,7 @@ export class AsideNav extends React.Component<AsideNavProps, AsideNavState> {
toggleExpand: this.toggleExpand,
depth,
classnames: cx,
...others,
...others
});
if (!dom) {
@ -165,7 +165,7 @@ export class AsideNav extends React.Component<AsideNavProps, AsideNavState> {
key={key}
className={cx(`AsideNav-item`, link.className, {
[`is-open`]: link.open,
[`is-active`]: link.active,
[`is-active`]: link.active
})}
>
{dom}
@ -188,7 +188,7 @@ export class AsideNav extends React.Component<AsideNavProps, AsideNavState> {
links.push(
React.cloneElement(prefix, {
...prefix.props,
key: `${index}-prefix`,
key: `${index}-prefix`
})
);
}
@ -211,7 +211,7 @@ export class AsideNav extends React.Component<AsideNavProps, AsideNavState> {
links.push(
React.cloneElement(affix, {
...affix.props,
key: `${index}-affix`,
key: `${index}-affix`
})
);
}

View File

@ -41,7 +41,7 @@ export class Button extends React.Component<ButtonProps> {
type: 'button',
placement: 'top',
tooltipTrigger: ['hover', 'focus'],
tooltipRootClose: false,
tooltipRootClose: false
};
renderButton() {
@ -79,7 +79,7 @@ export class Button extends React.Component<ButtonProps> {
[`Button--block`]: block,
[`Button--iconOnly`]: iconOnly,
'is-disabled': disabled,
'is-active': active,
'is-active': active
},
className
)}
@ -100,7 +100,7 @@ export class Button extends React.Component<ButtonProps> {
disabled,
disabledTip,
classPrefix,
classnames: cx,
classnames: cx
} = this.props;
return (

View File

@ -12,7 +12,7 @@ const sizeMap = {
sm: 'i-checks-sm',
lg: 'i-checks-lg',
small: 'i-checks-sm',
large: 'i-checks-lg',
large: 'i-checks-lg'
};
interface CheckboxProps {
@ -42,7 +42,7 @@ export class Checkbox extends React.Component<CheckboxProps, any> {
static defaultProps = {
trueValue: true,
falseValue: false,
type: 'checkbox',
type: 'checkbox'
};
@autobind
@ -70,7 +70,7 @@ export class Checkbox extends React.Component<CheckboxProps, any> {
readOnly,
checked,
type,
name,
name
} = this.props;
className = (className ? className : '') + (size && sizeMap[size] ? ` ${sizeMap[size]}` : '');
@ -80,7 +80,7 @@ export class Checkbox extends React.Component<CheckboxProps, any> {
className={cx(
`${ns}Checkbox ${ns}Checkbox--${type}`,
{
[`${ns}Checkbox--full`]: !partial,
[`${ns}Checkbox--full`]: !partial
},
className
)}
@ -106,4 +106,4 @@ export class Checkbox extends React.Component<CheckboxProps, any> {
}
}
export default themeable(Checkbox);
export default themeable(Checkbox);

View File

@ -120,7 +120,7 @@ export class Checkboxes extends React.PureComponent<CheckboxesProps, any> {
extractValue: false,
inline: false,
delimiter: ',',
columnsCount: 1, // 一行显示一个
columnsCount: 1 // 一行显示一个
};
toggleOption(option: Option) {
@ -130,7 +130,7 @@ export class Checkboxes extends React.PureComponent<CheckboxesProps, any> {
multiple: true,
valueField,
delimiter,
options,
options
});
let idx = valueArray.indexOf(option);
@ -140,7 +140,7 @@ export class Checkboxes extends React.PureComponent<CheckboxesProps, any> {
multiple: true,
valueField,
delimiter,
options,
options
})[0] || option;
idx = valueArray.indexOf(option);
}
@ -172,14 +172,14 @@ export class Checkboxes extends React.PureComponent<CheckboxesProps, any> {
placeholder,
columnsCount,
disabled,
inline,
inline
} = this.props;
let valueArray = value2array(value, {
multiple: true,
valueField,
delimiter,
options,
options
});
let body: Array<React.ReactNode> = [];
@ -219,6 +219,6 @@ export class Checkboxes extends React.PureComponent<CheckboxesProps, any> {
export default themeable(
uncontrollable(Checkboxes, {
value: 'onChange',
value: 'onChange'
})
);

View File

@ -7,8 +7,8 @@
import React from 'react';
import css = require('dom-helpers/style');
import {ClassNamesFn, themeable} from '../theme';
import Transition, { EXITED, ENTERING, EXITING } from 'react-transition-group/Transition';
import { autobind } from '../utils/helper';
import Transition, {EXITED, ENTERING, EXITING} from 'react-transition-group/Transition';
import {autobind} from '../utils/helper';
const collapseStyles: {
[propName: string]: string;
@ -19,23 +19,20 @@ const collapseStyles: {
};
export interface CollapseProps {
show?: boolean,
mountOnEnter?: boolean,
unmountOnExit?: boolean,
className?: string,
show?: boolean;
mountOnEnter?: boolean;
unmountOnExit?: boolean;
className?: string;
classPrefix: string;
classnames: ClassNamesFn;
}
export class Collapse extends React.Component<CollapseProps, any> {
static defaultProps: Pick<
CollapseProps,
'show' | 'mountOnEnter' | 'unmountOnExit'
> = {
static defaultProps: Pick<CollapseProps, 'show' | 'mountOnEnter' | 'unmountOnExit'> = {
show: false,
mountOnEnter: false,
unmountOnExit: false
}
};
contentDom: any;
contentRef = (ref: any) => (this.contentDom = ref);
@ -71,13 +68,7 @@ export class Collapse extends React.Component<CollapseProps, any> {
}
render() {
const {
show,
children,
classnames: cx,
mountOnEnter,
unmountOnExit
} = this.props;
const {show, children, classnames: cx, mountOnEnter, unmountOnExit} = this.props;
return (
<Transition
@ -91,7 +82,7 @@ export class Collapse extends React.Component<CollapseProps, any> {
onExit={this.handleExit}
onExiting={this.handleExiting}
>
{(status:string) => {
{(status: string) => {
if (status === ENTERING) {
this.contentDom.offsetWidth;
}
@ -103,11 +94,11 @@ export class Collapse extends React.Component<CollapseProps, any> {
(children as React.ReactElement).props.className,
collapseStyles[status]
)
})}
}
});
}}
</Transition>
);
}
}
export default themeable(Collapse);
export default themeable(Collapse);

View File

@ -40,13 +40,13 @@ export class ColorControl extends React.PureComponent<ColorProps, ColorControlSt
static defaultProps = {
format: 'hex',
clearable: true,
placeholder: '请选择颜色',
placeholder: '请选择颜色'
// closeOnSelect: true
};
state = {
isOpened: false,
isFocused: false,
inputValue: this.props.value || '',
inputValue: this.props.value || ''
};
popover: any;
closeTimer: number;
@ -74,21 +74,21 @@ export class ColorControl extends React.PureComponent<ColorProps, ColorControlSt
if (props.value !== nextProps.value) {
this.setState({
inputValue: nextProps.value || '',
inputValue: nextProps.value || ''
});
}
}
handleFocus() {
this.setState({
isFocused: true,
isFocused: true
});
}
handleBlur() {
this.setState({
isFocused: false,
inputValue: this.props.value,
inputValue: this.props.value
});
}
@ -106,7 +106,7 @@ export class ColorControl extends React.PureComponent<ColorProps, ColorControlSt
}
this.setState(
{
isOpened: true,
isOpened: true
},
fn
);
@ -114,7 +114,7 @@ export class ColorControl extends React.PureComponent<ColorProps, ColorControlSt
close() {
this.setState({
isOpened: false,
isOpened: false
});
}
@ -132,7 +132,7 @@ export class ColorControl extends React.PureComponent<ColorProps, ColorControlSt
this.setState(
{
inputValue: e.currentTarget.value,
inputValue: e.currentTarget.value
},
() => {
const dom: HTMLElement = this.preview.current as HTMLElement;
@ -148,7 +148,7 @@ export class ColorControl extends React.PureComponent<ColorProps, ColorControlSt
handleChange(color: ColorResult) {
const {
onChange,
format,
format
// closeOnSelect
} = this.props;
@ -191,7 +191,7 @@ export class ColorControl extends React.PureComponent<ColorProps, ColorControlSt
`ColorPicker`,
{
'is-disabled': disabled,
'is-focused': isFocused,
'is-focused': isFocused
},
className
)}
@ -251,6 +251,6 @@ export class ColorControl extends React.PureComponent<ColorProps, ColorControlSt
export default themeable(
uncontrollable(ColorControl, {
value: 'onChange',
value: 'onChange'
})
);

View File

@ -49,15 +49,19 @@ class BaseDatePicker extends ReactDatePicker {
return props;
};
}
// TODO: Make a function or clean up this code,
// logic right now is really hard to follow
let className = 'rdt' + (this.props.className ?
( Array.isArray( this.props.className ) ?
' ' + this.props.className.join( ' ' ) : ' ' + this.props.className) : ''),
children:Array<any> = [];
if ( this.props.input ) {
// TODO: Make a function or clean up this code,
// logic right now is really hard to follow
let className =
'rdt' +
(this.props.className
? Array.isArray(this.props.className)
? ' ' + this.props.className.join(' ')
: ' ' + this.props.className
: ''),
children: Array<any> = [];
if (this.props.input) {
var finalInputProps = {
type: 'text',
className: 'form-control',
@ -69,23 +73,18 @@ class BaseDatePicker extends ReactDatePicker {
...this.props.inputProps
};
if ( this.props.renderInput ) {
children = [(
<div key='i'>
{this.props.renderInput(finalInputProps, this.openCalendar, this.closeCalendar)}
</div>
)];
} else {
children= [
<input key="i" {...finalInputProps} />
if (this.props.renderInput) {
children = [
<div key="i">{this.props.renderInput(finalInputProps, this.openCalendar, this.closeCalendar)}</div>
];
} else {
children = [<input key="i" {...finalInputProps} />];
}
} else {
className += ' rdtStatic';
}
if ( this.state.open )
className += ' rdtOpen';
if (this.state.open) className += ' rdtOpen';
return (
<div className={className}>
@ -127,7 +126,7 @@ interface CustomDaysViewProps {
class CustomDaysView extends React.Component<CustomDaysViewProps> {
static defaultProps = {
classPrefix: 'a-',
classPrefix: 'a-'
};
constructor(props: CustomDaysViewProps) {
@ -183,7 +182,7 @@ class CustomDaysView extends React.Component<CustomDaysViewProps> {
this.props.setDateTimeState({
viewDate,
selectedDate: viewDate.clone(),
selectedDate: viewDate.clone()
});
return;
}
@ -206,7 +205,7 @@ class CustomDaysView extends React.Component<CustomDaysViewProps> {
viewDate: viewDate
.clone()
.month(option.value)
.startOf('month'),
.startOf('month')
});
}
@ -223,7 +222,7 @@ class CustomDaysView extends React.Component<CustomDaysViewProps> {
const viewDate = this.props.viewDate;
const newDate = viewDate.clone().year(option.value);
this.props.setDateTimeState({
viewDate: newDate[newDate.isBefore(viewDate) ? 'endOf' : 'startOf']('year'),
viewDate: newDate[newDate.isBefore(viewDate) ? 'endOf' : 'startOf']('year')
});
}
@ -233,7 +232,7 @@ class CustomDaysView extends React.Component<CustomDaysViewProps> {
this.props.setDateTimeState({
viewDate: date.clone(),
selectedDate: date.clone(),
selectedDate: date.clone()
});
if (!this.props.requiredConfirm) {
@ -245,7 +244,7 @@ class CustomDaysView extends React.Component<CustomDaysViewProps> {
const date = this.props.viewDate.clone();
this.props.setDateTimeState({
selectedDate: date,
selectedDate: date
});
this.props.onChange(date);
this.props.onClose && this.props.onClose();
@ -276,12 +275,12 @@ class CustomDaysView extends React.Component<CustomDaysViewProps> {
let currentYear = date.clone().set({
year: year,
month: irrelevantMonth,
date: irrelevantDate,
date: irrelevantDate
});
const noOfDaysInYear = parseInt(currentYear.endOf('year').format('DDD'), 10);
const daysInYear = Array.from(
{
length: noOfDaysInYear,
length: noOfDaysInYear
},
(e, i) => i + 1
);
@ -303,12 +302,12 @@ class CustomDaysView extends React.Component<CustomDaysViewProps> {
let currentYear = date.clone().set({
year: year,
month: irrelevantMonth,
date: irrelevantDate,
date: irrelevantDate
});
const noOfDaysInYear = parseInt(currentYear.endOf('year').format('DDD'), 10);
const daysInYear = Array.from(
{
length: noOfDaysInYear,
length: noOfDaysInYear
},
(e, i) => i + 1
);
@ -327,7 +326,7 @@ class CustomDaysView extends React.Component<CustomDaysViewProps> {
value={date.year()}
options={years.map(year => ({
label: `${year}`,
value: year,
value: year
}))}
onChange={this.handleYearChange}
clearable={false}
@ -348,7 +347,7 @@ class CustomDaysView extends React.Component<CustomDaysViewProps> {
const currentMonth = date.clone().set({
year,
month: i,
date: 1,
date: 1
});
const noOfDaysInMonth = parseInt(currentMonth.endOf('month').format('D'), 10);
@ -369,7 +368,7 @@ class CustomDaysView extends React.Component<CustomDaysViewProps> {
value={date.month()}
options={days.map(day => ({
label: `${day + 1}`,
value: day,
value: day
}))}
onChange={this.handleMonthChange}
clearable={false}
@ -484,7 +483,7 @@ class CustomDaysView extends React.Component<CustomDaysViewProps> {
dayProps = {
key: prevMonth.format('M_D'),
'data-value': prevMonth.date(),
className: classes,
className: classes
};
if (!isDisabled) dayProps.onClick = this.handleDayChange;
@ -532,7 +531,7 @@ class CustomDaysView extends React.Component<CustomDaysViewProps> {
</tr>
</thead>,
<tbody key="tb">{this.renderDays()}</tbody>,
<tbody key="tb">{this.renderDays()}</tbody>
];
footer && tableChildren.push(footer);
@ -545,13 +544,12 @@ class CustomDaysView extends React.Component<CustomDaysViewProps> {
}
}
const availableShortcuts: {[propName: string]: any} = {
today: {
label: '今天',
date: (now: moment.Moment) => {
return now.startOf('day');
},
}
},
yesterday: {
@ -586,15 +584,13 @@ const availableShortcuts: {[propName: string]: any} = {
label: '90天前',
date: (now: moment.Moment) => {
return now.add(-90, 'days');
},
}
},
thisweek: {
label: '本周一',
date: (now: moment.Moment) => {
return now
.startOf('week')
.add(-1, 'weeks');
return now.startOf('week').add(-1, 'weeks');
}
},
@ -602,7 +598,7 @@ const availableShortcuts: {[propName: string]: any} = {
label: '本月初',
date: (now: moment.Moment) => {
return now.startOf('month');
},
}
},
prevmonth: {
@ -616,7 +612,7 @@ const availableShortcuts: {[propName: string]: any} = {
label: '上个季节初',
date: (now: moment.Moment) => {
return now.startOf('quarter').add(-1, 'quarter');
},
}
},
thisquarter: {
@ -658,14 +654,13 @@ const availableShortcuts: {[propName: string]: any} = {
label: '90天后',
date: (now: moment.Moment) => {
return now.add(90, 'days');
},
}
},
endofthisweek: {
label: '本周日',
date: (now: moment.Moment) => {
return now
.endOf('week');
return now.endOf('week');
}
},
@ -673,7 +668,7 @@ const availableShortcuts: {[propName: string]: any} = {
label: '本月底',
date: (now: moment.Moment) => {
return now.endOf('month');
},
}
}
};
@ -711,12 +706,14 @@ export interface DatePickerState {
export class DatePicker extends React.Component<DateProps, DatePickerState> {
static defaultProps: Pick<DateProps, 'viewMode' | 'shortcuts'> = {
viewMode: 'days',
shortcuts: '',
shortcuts: ''
};
state: DatePickerState = {
isOpened: false,
isFocused: false,
value: this.props.value ? (this.props.utc ? moment.utc : moment)(this.props.value, this.props.format) : undefined,
value: this.props.value
? (this.props.utc ? moment.utc : moment)(this.props.value, this.props.format)
: undefined
};
constructor(props: DateProps) {
super(props);
@ -741,7 +738,9 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
componentWillReceiveProps(nextProps: DateProps) {
if (this.props.value !== nextProps.value) {
this.setState({
value: nextProps.value ? (nextProps.utc ? moment.utc : moment)(nextProps.value, nextProps.format) : undefined,
value: nextProps.value
? (nextProps.utc ? moment.utc : moment)(nextProps.value, nextProps.format)
: undefined
});
}
}
@ -756,13 +755,13 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
handleFocus() {
this.setState({
isFocused: true,
isFocused: true
});
}
handleBlur() {
this.setState({
isFocused: false,
isFocused: false
});
}
@ -785,7 +784,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
this.props.disabled ||
this.setState(
{
isOpened: true,
isOpened: true
},
fn
);
@ -793,7 +792,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
close() {
this.setState({
isOpened: false,
isOpened: false
});
}
@ -824,7 +823,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
}
}
selectRannge(item:any) {
selectRannge(item: any) {
const now = moment();
this.handleChange(item.date(now));
this.close();
@ -886,7 +885,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
`${ns}DatePicker`,
{
'is-disabled': disabled,
'is-focused': this.state.isFocused,
'is-focused': this.state.isFocused
},
className
)}
@ -942,7 +941,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
))}
</ul>
) : null}
<BaseDatePicker
value={date}
onChange={this.handleChange}

View File

@ -8,7 +8,7 @@ import React = require('react');
import moment = require('moment');
import {findDOMNode} from 'react-dom';
import cx from 'classnames';
import { Icon } from './icons';
import {Icon} from './icons';
import Overlay from './Overlay';
import {BaseDatePicker} from './DatePicker';
import PopOver from './PopOver';
@ -51,7 +51,7 @@ const availableRanges: {[propName: string]: any} = {
},
endDate: (now: moment.Moment) => {
return now;
},
}
},
yesterday: {
@ -61,7 +61,7 @@ const availableRanges: {[propName: string]: any} = {
},
endDate: (now: moment.Moment) => {
return now.add(-1, 'days').endOf('day');
},
}
},
'1dayago': {
@ -71,7 +71,7 @@ const availableRanges: {[propName: string]: any} = {
},
endDate: (now: moment.Moment) => {
return now;
},
}
},
'7daysago': {
@ -81,7 +81,7 @@ const availableRanges: {[propName: string]: any} = {
},
endDate: (now: moment.Moment) => {
return now;
},
}
},
'90daysago': {
@ -91,22 +91,20 @@ const availableRanges: {[propName: string]: any} = {
},
endDate: (now: moment.Moment) => {
return now;
},
}
},
prevweek: {
label: '上周',
startDate: (now: moment.Moment) => {
return now
.startOf('week')
.add(-1, 'weeks');
return now.startOf('week').add(-1, 'weeks');
},
endDate: (now: moment.Moment) => {
return now
.startOf('week')
.add(-1, 'days')
.endOf('day');
},
}
},
thismonth: {
@ -116,7 +114,7 @@ const availableRanges: {[propName: string]: any} = {
},
endDate: (now: moment.Moment) => {
return now;
},
}
},
prevmonth: {
@ -129,7 +127,7 @@ const availableRanges: {[propName: string]: any} = {
.startOf('month')
.add(-1, 'day')
.endOf('day');
},
}
},
prevquarter: {
@ -142,7 +140,7 @@ const availableRanges: {[propName: string]: any} = {
.startOf('quarter')
.add(-1, 'day')
.endOf('day');
},
}
},
thisquarter: {
@ -152,8 +150,8 @@ const availableRanges: {[propName: string]: any} = {
},
endDate: (now: moment.Moment) => {
return now;
},
},
}
}
};
export class DateRangePicker extends React.Component<DateRangePickerProps, DateRangePickerState> {
@ -166,7 +164,7 @@ export class DateRangePicker extends React.Component<DateRangePickerProps, DateR
delimiter: ',',
ranges: 'yesterday,7daysago,prevweek,thismonth,prevmonth,prevquarter',
iconClassName: 'fa fa-calendar',
resetValue: '',
resetValue: ''
};
innerDom: any;
@ -187,7 +185,7 @@ export class DateRangePicker extends React.Component<DateRangePickerProps, DateR
if (!value) {
return {
startDate: undefined,
endDate: undefined,
endDate: undefined
};
}
@ -197,7 +195,7 @@ export class DateRangePicker extends React.Component<DateRangePickerProps, DateR
return {
startDate: value[0] ? moment(value[0], format) : undefined,
endDate: value[1] ? moment(value[1], format) : undefined,
endDate: value[1] ? moment(value[1], format) : undefined
};
}
@ -226,7 +224,7 @@ export class DateRangePicker extends React.Component<DateRangePickerProps, DateR
this.state = {
isOpened: false,
isFocused: false,
...DateRangePicker.unFormatValue(value, format, joinValues, delimiter),
...DateRangePicker.unFormatValue(value, format, joinValues, delimiter)
};
}
@ -236,7 +234,7 @@ export class DateRangePicker extends React.Component<DateRangePickerProps, DateR
if (props.value !== value) {
this.setState({
...DateRangePicker.unFormatValue(value, format, joinValues, delimiter),
...DateRangePicker.unFormatValue(value, format, joinValues, delimiter)
});
}
}
@ -259,13 +257,13 @@ export class DateRangePicker extends React.Component<DateRangePickerProps, DateR
handleFocus() {
this.setState({
isFocused: true,
isFocused: true
});
}
handleBlur() {
this.setState({
isFocused: false,
isFocused: false
});
}
@ -275,14 +273,14 @@ export class DateRangePicker extends React.Component<DateRangePickerProps, DateR
}
this.setState({
isOpened: true,
isOpened: true
});
}
close() {
this.setState(
{
isOpened: false,
isOpened: false
},
this.blur
);
@ -314,7 +312,7 @@ export class DateRangePicker extends React.Component<DateRangePickerProps, DateR
DateRangePicker.formatValue(
{
startDate: this.state.startDate,
endDate: this.state.endDate,
endDate: this.state.endDate
},
this.props.format,
this.props.joinValues,
@ -326,14 +324,14 @@ export class DateRangePicker extends React.Component<DateRangePickerProps, DateR
handleStartChange(newValue: any) {
this.setState({
startDate: newValue.clone(),
startDate: newValue.clone()
});
}
handleEndChange(newValue: any) {
newValue = !this.state.endDate && !this.props.timeFormat ? newValue.endOf('day') : newValue;
this.setState({
endDate: newValue.clone(),
endDate: newValue.clone()
});
}
@ -344,7 +342,7 @@ export class DateRangePicker extends React.Component<DateRangePickerProps, DateR
const now = moment();
this.setState({
startDate: range.startDate(now.clone()),
endDate: range.endDate(now.clone()),
endDate: range.endDate(now.clone())
});
}
@ -403,7 +401,7 @@ export class DateRangePicker extends React.Component<DateRangePickerProps, DateR
timeFormat,
ranges,
disabled,
iconClassName,
iconClassName
} = this.props;
const {isOpened, isFocused, startDate, endDate} = this.state;
@ -425,7 +423,7 @@ export class DateRangePicker extends React.Component<DateRangePickerProps, DateR
`${ns}DateRangePicker`,
{
'is-disabled': disabled,
'is-focused': isFocused,
'is-focused': isFocused
},
className
)}
@ -520,7 +518,7 @@ export class DateRangePicker extends React.Component<DateRangePickerProps, DateR
<div key="button" className={`${ns}DateRangePicker-actions`}>
<a
className={cx('rdtBtn rdtBtnConfirm', {
'is-disabled': !this.state.startDate || !this.state.endDate,
'is-disabled': !this.state.startDate || !this.state.endDate
})}
onClick={this.confirm}
>

View File

@ -38,20 +38,20 @@ const fadeStyles: {
[propName: string]: string;
} = {
[ENTERING]: 'in',
[ENTERED]: 'in',
[ENTERED]: 'in'
};
export class Drawer extends React.Component<DrawerProps, DrawerState> {
static defaultProps: Pick<
DrawerProps,
'container' | 'position' | 'size' | 'overlay' | 'disableOnClickOutside' | 'enableOnClickOutside'
> = {
container: document.body,
position: 'left',
size: 'md',
overlay: true,
disableOnClickOutside: noop,
enableOnClickOutside: noop,
};
container: document.body,
position: 'left',
size: 'md',
overlay: true,
disableOnClickOutside: noop,
enableOnClickOutside: noop
};
contentDom: any;
@ -111,7 +111,7 @@ export class Drawer extends React.Component<DrawerProps, DrawerState> {
size,
onHide,
disabled,
overlay,
overlay
} = this.props;
return (
@ -141,7 +141,7 @@ export class Drawer extends React.Component<DrawerProps, DrawerState> {
{
[`${ns}Drawer--${position}`]: position,
[`${ns}Drawer--${size}`]: size,
[`${ns}Drawer--noOverlay`]: !overlay,
[`${ns}Drawer--noOverlay`]: !overlay
},
className
)}

View File

@ -20,7 +20,7 @@ export interface HtmlProps {
export class Html extends React.Component<HtmlProps> {
static defaultProps = {
inline: true,
inline: true
};
dom: any;

View File

@ -53,7 +53,7 @@ export function Layout({
boxed,
classnames: cx,
bodyClassName
}:LayoutProps) {
}: LayoutProps) {
let body = <div className={cx(`Layout-body`, contentClassName)}>{children}</div>;
if (aside) {
@ -68,7 +68,7 @@ export function Layout({
bodyClassName && document.body.classList.add(bodyClassName);
return () => {
bodyClassName && document.body.classList.remove(bodyClassName)
bodyClassName && document.body.classList.remove(bodyClassName);
};
}, [bodyClassName]);
@ -82,7 +82,7 @@ export function Layout({
'Layout--folded': folded,
'Layout--offScreen': offScreen,
[`Layout--${size}`]: size,
'Layout--noFooter': !footer,
'Layout--noFooter': !footer
})}
>
{header ? <div className={cx('Layout-header')}>{header}</div> : null}
@ -111,7 +111,7 @@ Layout.defaultProps = {
asideClassName: '',
headerFixed: true,
offScreen: false,
footer: false,
footer: false
};
export default themeable(Layout);
export default themeable(Layout);

View File

@ -26,10 +26,10 @@ export default class LazyComponent extends React.Component<LazyComponentProps, L
static defaultProps = {
placeholder: '加载中...',
unMountOnHidden: false,
partialVisibility: true,
partialVisibility: true
};
mounted:boolean = false;
mounted: boolean = false;
constructor(props: LazyComponentProps) {
super(props);
@ -37,7 +37,7 @@ export default class LazyComponent extends React.Component<LazyComponentProps, L
this.state = {
visible: false,
component: props.component as React.ReactType,
component: props.component as React.ReactType
};
}
@ -51,7 +51,7 @@ export default class LazyComponent extends React.Component<LazyComponentProps, L
handleVisibleChange(visible: boolean) {
this.setState({
visible: visible,
visible: visible
});
if (!visible || this.state.component || !this.props.getComponent) {
@ -60,15 +60,20 @@ export default class LazyComponent extends React.Component<LazyComponentProps, L
this.props
.getComponent()
.then(component =>
this.mounted && typeof component === 'function' && this.setState({
component: component,
})
.then(
component =>
this.mounted &&
typeof component === 'function' &&
this.setState({
component: component
})
)
.catch(reason =>
this.mounted && this.setState({
component: () => <div className="alert alert-danger">{String(reason)}</div>,
})
.catch(
reason =>
this.mounted &&
this.setState({
component: () => <div className="alert alert-danger">{String(reason)}</div>
})
);
}

View File

@ -30,13 +30,13 @@ const fadeStyles: {
[propName: string]: string;
} = {
[ENTERING]: 'in',
[ENTERED]: 'in',
[ENTERED]: 'in'
};
export class Modal extends React.Component<ModalProps, ModalState> {
static defaultProps = {
container: document.body,
size: '',
overlay: true,
overlay: true
};
contentDom: any;
@ -105,7 +105,7 @@ export class Modal extends React.Component<ModalProps, ModalState> {
className={cx(
`amis-dialog-widget ${ns}Modal`,
{
[`${ns}Modal--${size}`]: size,
[`${ns}Modal--${size}`]: size
},
className
)}

View File

@ -19,7 +19,7 @@ Position.prototype.updatePosition = function(target: any) {
positionLeft: 0,
positionTop: 0,
arrowOffsetLeft: null,
arrowOffsetTop: null,
arrowOffsetTop: null
});
}

View File

@ -7,7 +7,7 @@
import React from 'react';
import {findDOMNode} from 'react-dom';
import {ClassNamesFn, themeable} from '../theme';
import { camel } from '../utils/helper';
import {camel} from '../utils/helper';
export interface Offset {
x: number;
@ -41,18 +41,18 @@ export class PopOver extends React.PureComponent<PopOverPorps, PopOverState> {
className: '',
offset: {
x: 0,
y: 0,
y: 0
},
overlay: false,
placement: 'bottom',
placement: 'bottom'
};
state = {
xOffset: 0,
yOffset: 0,
yOffset: 0
};
parent:HTMLElement;
parent: HTMLElement;
componentDidMount() {
this.mayUpdateOffset();
@ -79,14 +79,14 @@ export class PopOver extends React.PureComponent<PopOverPorps, PopOverState> {
offset = getOffset((findDOMNode(this) as HTMLElement).getBoundingClientRect(), {
x,
y,
placement,
placement
});
} else {
offset = getOffset as Offset;
}
this.setState({
xOffset: (offset && offset.x) ? (offset as Offset).x : 0,
yOffset: (offset && offset.y) ? (offset as Offset).y : 0,
xOffset: offset && offset.x ? (offset as Offset).x : 0,
yOffset: offset && offset.y ? (offset as Offset).y : 0
});
}
@ -114,11 +114,15 @@ export class PopOver extends React.PureComponent<PopOverPorps, PopOverState> {
display: 'block',
...style,
top: (positionTop as number) + yOffset,
left: (positionLeft as number) + xOffset,
left: (positionLeft as number) + xOffset
};
return (
<div className={cx(`${ns}PopOver`, className, `${ns}PopOver--${camel(activePlacement)}`)} style={outerStyle} {...rest}>
<div
className={cx(`${ns}PopOver`, className, `${ns}PopOver--${camel(activePlacement)}`)}
style={outerStyle}
{...rest}
>
{overlay ? <div className={`${ns}PopOver-overlay`} onClick={onHide} /> : null}
{children}
</div>

View File

@ -39,24 +39,17 @@ export class Radios extends React.Component<RadioProps, any> {
static defaultProps = {
joinValues: true,
clearable: false,
columnsCount: 1, // 一行显示一个
columnsCount: 1 // 一行显示一个
};
toggleOption(option: Option) {
const {
value,
onChange,
valueField,
clearable,
delimiter,
options
} = this.props;
const {value, onChange, valueField, clearable, delimiter, options} = this.props;
let valueArray = value2array(value, {
multiple: false,
delimiter,
valueField,
options,
options
});
const idx = valueArray.indexOf(option);
@ -71,36 +64,26 @@ export class Radios extends React.Component<RadioProps, any> {
onChange && onChange(newValue);
}
renderGroup(option:Option, index:number, valueArray: Array<Option>) {
const {
classnames: cx
} = this.props;
renderGroup(option: Option, index: number, valueArray: Array<Option>) {
const {classnames: cx} = this.props;
return (
<div key={index} className={cx("RadiosControl-group", option.className)}>
<label className={cx("RadiosControl-groupLabel", option.labelClassName)}>{option.label}</label>
<div key={index} className={cx('RadiosControl-group', option.className)}>
<label className={cx('RadiosControl-groupLabel', option.labelClassName)}>{option.label}</label>
{
option.children && option.children.length
? option.children.map((option, index) => this.renderItem(option, index, valueArray))
: null
}
{option.children && option.children.length
? option.children.map((option, index) => this.renderItem(option, index, valueArray))
: null}
</div>
);
}
renderItem(option:Option, index:number, valueArray: Array<Option>) {
renderItem(option: Option, index: number, valueArray: Array<Option>) {
if (option.children) {
return this.renderGroup(option, index, valueArray);
}
const {
disabled,
inline,
itemClassName,
classnames: cx
} = this.props;
const {disabled, inline, itemClassName, classnames: cx} = this.props;
return (
<Checkbox
@ -129,14 +112,14 @@ export class Radios extends React.Component<RadioProps, any> {
inline,
delimiter,
valueField,
classPrefix,
classPrefix
} = this.props;
let valueArray = value2array(value, {
multiple: false,
delimiter,
valueField,
options,
options
});
let body: Array<React.ReactNode> = [];
@ -150,7 +133,9 @@ export class Radios extends React.Component<RadioProps, any> {
body = chunk(body, columnsCount).map((group, groupIndex) => (
<div className={cx('Grid')} key={groupIndex}>
{Array.from({length: columnsCount as number}).map((_, index) => (
<div key={index} className={cx(cellClassName)}>{group[index]}</div>
<div key={index} className={cx(cellClassName)}>
{group[index]}
</div>
))}
</div>
));
@ -162,6 +147,6 @@ export class Radios extends React.Component<RadioProps, any> {
export default themeable(
uncontrollable(Radios, {
value: 'onChange',
value: 'onChange'
})
);

View File

@ -16,10 +16,12 @@ interface RangeProps extends RendererProps {
className?: string;
min: number;
max: number;
value: {
min: number,
max: number
} | number;
value:
| {
min: number;
max: number;
}
| number;
classPrefix: string;
classnames: ClassNamesFn;
}
@ -27,7 +29,7 @@ interface RangeProps extends RendererProps {
export class Range extends React.Component<RangeProps, any> {
static defaultProps: Partial<RangeProps> = {
min: 1,
max: 100,
max: 100
};
render() {
@ -43,7 +45,7 @@ export class Range extends React.Component<RangeProps, any> {
slider: `${ns}InputRange-slider`,
sliderContainer: `${ns}InputRange-sliderContainer`,
track: `${ns}InputRange-track ${ns}InputRange-track--background`,
valueLabel: `${ns}InputRange-label ${ns}InputRange-label--value`,
valueLabel: `${ns}InputRange-label ${ns}InputRange-label--value`
};
return (
@ -62,6 +64,6 @@ export class Range extends React.Component<RangeProps, any> {
export default themeable(
uncontrollable(Range, {
value: 'onChange',
value: 'onChange'
})
);

View File

@ -33,7 +33,7 @@ export class Rating extends React.Component<RatingProps, any> {
value: 0,
count: 5,
char: '★',
size: 24,
size: 24
};
constructor(props: RatingProps) {
@ -44,8 +44,8 @@ export class Rating extends React.Component<RatingProps, any> {
stars: [],
halfStar: {
at: Math.floor(props.value),
hidden: props.half && props.value % 1 < 0.5,
},
hidden: props.half && props.value % 1 < 0.5
}
};
this.getRate = this.getRate.bind(this);
@ -59,7 +59,7 @@ export class Rating extends React.Component<RatingProps, any> {
componentDidMount() {
const {value} = this.state;
this.setState({
stars: this.getStars(value),
stars: this.getStars(value)
});
}
@ -69,8 +69,8 @@ export class Rating extends React.Component<RatingProps, any> {
value: props.value,
halfStar: {
at: Math.floor(props.value),
hidden: props.half && props.value % 1 < 0.5,
},
hidden: props.half && props.value % 1 < 0.5
}
});
}
@ -94,7 +94,7 @@ export class Rating extends React.Component<RatingProps, any> {
const {count} = this.props;
for (let i = 0; i < count; i++) {
stars.push({
active: i <= activeCount - 1,
active: i <= activeCount - 1
});
}
return stars;
@ -110,14 +110,14 @@ export class Rating extends React.Component<RatingProps, any> {
this.setState({
halfStar: {
at: index,
hidden: isAtHalf,
},
hidden: isAtHalf
}
});
} else {
index = index + 1;
}
this.setState({
stars: this.getStars(index),
stars: this.getStars(index)
});
}
@ -136,12 +136,12 @@ export class Rating extends React.Component<RatingProps, any> {
this.setState({
halfStar: {
at: Math.floor(value),
hidden: value % 1 === 0, // check value is decimal or not
},
hidden: value % 1 === 0 // check value is decimal or not
}
});
}
this.setState({
stars: this.getStars(),
stars: this.getStars()
});
}
@ -157,15 +157,15 @@ export class Rating extends React.Component<RatingProps, any> {
this.setState({
halfStar: {
at: index,
hidden: isAtHalf,
},
hidden: isAtHalf
}
});
} else {
value = index = index + 1;
}
this.setState({
value: value,
stars: this.getStars(index),
stars: this.getStars(index)
});
onChange && onChange(value);
}
@ -177,7 +177,7 @@ export class Rating extends React.Component<RatingProps, any> {
let className = cx('Rating', {
'Rating-half': half && !halfStar.hidden && halfStar.at === i,
'is-active': star.active,
'is-disabled': readOnly,
'is-disabled': readOnly
});
return (

View File

@ -56,7 +56,7 @@ export default class FroalaEditor extends React.Component<any, any> {
$editor: any = null;
config: any = {
immediateReactModelUpdate: false,
reactIgnoreAttrs: null,
reactIgnoreAttrs: null
};
editorInitialized: boolean = false;
oldModel: any = null;
@ -203,8 +203,8 @@ export default class FroalaEditor extends React.Component<any, any> {
url_regex: '',
url_text: '',
html:
'<span class="fr-video fr-dvb fr-draggable" contenteditable="false" draggable="true"><video class="fr-draggable" controls="" data-msg="ok" data-status="0" src="{url}" style="width: 600px;"></video></span>',
},
'<span class="fr-video fr-dvb fr-draggable" contenteditable="false" draggable="true"><video class="fr-draggable" controls="" data-msg="ok" data-status="0" src="{url}" style="width: 600px;"></video></span>'
}
];
($ as any).FE.LANGUAGE['zh_cn'] = {
translation: {
@ -430,7 +430,7 @@ export default class FroalaEditor extends React.Component<any, any> {
Decrease: '\u5220\u9664\u5f15\u7528',
// Quick Insert
'Quick Insert': '\u5feb\u63d2',
'Quick Insert': '\u5feb\u63d2'
},
direction: 'ltr',
direction: 'ltr'
};

View File

@ -44,7 +44,7 @@ export interface OptionProps {
delimiter?: string;
clearable?: boolean;
placeholder?: string;
autoFill?: {[propName:string]: any}
autoFill?: {[propName: string]: any};
}
export type OptionValue = string | number | null | undefined | Option;
@ -67,7 +67,7 @@ export function value2array(value: OptionValue | Array<OptionValue>, props: Part
} else if (Array.isArray(value)) {
value = value[0];
}
let expandedValue = expandValue(value as OptionValue, props);
return expandedValue ? [expandedValue] : [];
}
@ -92,18 +92,18 @@ export function normalizeOptions(options: string | {[propName: string]: string}
if (typeof options === 'string') {
return options.split(',').map(item => ({
label: item,
value: item,
value: item
}));
} else if (Array.isArray(options as Array<string>) && typeof (options as Array<string>)[0] === 'string') {
return (options as Array<string>).map(item => ({
label: item,
value: item,
value: item
}));
} else if (Array.isArray(options as Options)) {
return (options as Options).map(item => {
let option = {
...item,
value: item && item.value,
value: item && item.value
};
if (typeof option.children !== 'undefined') {
@ -115,7 +115,7 @@ export function normalizeOptions(options: string | {[propName: string]: string}
} else if (isPlainObject(options)) {
return Object.keys(options).map(key => ({
label: (options as {[propName: string]: string})[key] as string,
value: key,
value: key
}));
}
@ -154,7 +154,7 @@ interface SelectProps {
checkAll?: boolean;
checkAllLabel?: string;
defaultCheckAll?: boolean;
simpleValue?: boolean;
simpleValue?: boolean;
}
interface SelectState {
@ -214,7 +214,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
isFocused: false,
inputValue: '',
highlightedIndex: -1,
selection: value2array(props.value, props),
selection: value2array(props.value, props)
};
}
@ -224,9 +224,12 @@ export class Select extends React.Component<SelectProps, SelectState> {
if (multiple && checkAll && defaultCheckAll && options.length) {
selection = union(options, selection);
this.setState({
selection: selection
}, () => onChange(simpleValue ? selection.map(item => item.value) : selection));
this.setState(
{
selection: selection
},
() => onChange(simpleValue ? selection.map(item => item.value) : selection)
);
}
loadOptions && loadOptions('');
@ -237,7 +240,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
if (props.value !== nextProps.value || JSON.stringify(props.options) !== JSON.stringify(nextProps.options)) {
this.setState({
selection: value2array(nextProps.value, nextProps),
selection: value2array(nextProps.value, nextProps)
});
}
}
@ -245,24 +248,24 @@ export class Select extends React.Component<SelectProps, SelectState> {
open() {
this.props.disabled ||
this.setState({
isOpen: true,
isOpen: true
});
}
close() {
this.setState({
isOpen: false,
isOpen: false
});
}
toggle(e?: React.MouseEvent<HTMLDivElement>) {
if (e && this.menu.current && this.menu.current.contains(e.target as HTMLElement)) {
if (e && this.menu.current && this.menu.current.contains(e.target as HTMLElement)) {
return;
}
this.props.disabled ||
this.setState({
isOpen: !this.state.isOpen,
isOpen: !this.state.isOpen
});
}
@ -270,7 +273,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
this.props.disabled ||
this.setState(
{
isFocused: true,
isFocused: true
},
this.focus
);
@ -281,7 +284,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
onBlur(e: any) {
this.setState({
isFocused: false,
inputValue: '',
inputValue: ''
});
this.props.onBlur && this.props.onBlur(e);
@ -333,7 +336,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
this.setState(
{
inputValue: evt.currentTarget.value,
inputValue: evt.currentTarget.value
},
() => loadOptions && loadOptions(this.state.inputValue)
);
@ -456,7 +459,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
multiple,
classnames: cx,
checkAll,
checkAllLabel,
checkAllLabel
} = this.props;
const {selection} = this.state;
@ -465,7 +468,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
let filtedOptions: Array<Option> =
inputValue && isOpen && !loadOptions
? matchSorter(options, inputValue, {
keys: [labelField || 'label', valueField || 'value'],
keys: [labelField || 'label', valueField || 'value']
})
: options.concat();
@ -480,11 +483,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
}
}
if (
inputValue &&
creatable &&
!find(options, (item) => item[labelField || 'label'] == inputValue)
) {
if (inputValue && creatable && !find(options, item => item[labelField || 'label'] == inputValue)) {
filtedOptions.unshift({
[labelField]: inputValue,
[valueField]: inputValue,
@ -507,7 +506,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
) : null}
{filtedOptions.length ? (
filtedOptions.map((item, index) => {
const checked = checkAll ? selection.some((o:Option) => o.value == item.value) : false;
const checked = checkAll ? selection.some((o: Option) => o.value == item.value) : false;
return (
<div
@ -515,14 +514,14 @@ export class Select extends React.Component<SelectProps, SelectState> {
key: index,
index,
item,
disabled: item.disabled,
disabled: item.disabled
})}
className={cx(`Select-option`, {
'is-disabled': item.disabled,
'is-highlight': highlightedIndex === index,
'is-active':
selectedItem === item ||
(Array.isArray(selectedItem) && ~selectedItem.indexOf(item)),
(Array.isArray(selectedItem) && ~selectedItem.indexOf(item))
})}
>
{checkAll ? (
@ -540,10 +539,16 @@ export class Select extends React.Component<SelectProps, SelectState> {
) : item.isNew ? (
promptTextCreator(item.label as string)
) : (
<span>{item.disabled ? item.label : highlight(item[labelField], inputValue as string, cx('Select-option-hl'))}{item.tip}</span>
<span>
{item.disabled
? item.label
: highlight(item[labelField], inputValue as string, cx('Select-option-hl'))}
{item.tip}
</span>
)}
</div>
)})
);
})
) : (
<div className={cx('Select-option Select-option--placeholder')}>{noResultsText}</div>
)}
@ -578,7 +583,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
spinnerClassName,
clearable,
labelField,
disabled,
disabled
} = this.props;
const selection = this.state.selection;
@ -612,7 +617,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
[`Select--searchable`]: searchable,
'is-opened': isOpen,
'is-focused': this.state.isFocused,
'is-disabled': disabled,
'is-disabled': disabled
},
className
)}
@ -631,7 +636,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
}
},
onChange: this.handleInputChange,
ref: this.inputRef,
ref: this.inputRef
})}
/>
) : null}
@ -659,6 +664,6 @@ export class Select extends React.Component<SelectProps, SelectState> {
export default themeable(
uncontrollable(Select, {
value: 'onChange',
value: 'onChange'
})
);

View File

@ -24,7 +24,7 @@ export class Spinner extends React.Component<SpinnerProps, object> {
overlay: false,
spinnerClassName: '',
mode: '',
size: '',
size: ''
};
render() {
@ -34,7 +34,7 @@ export class Spinner extends React.Component<SpinnerProps, object> {
<div
className={cx(`${ns}Spinner`, spinnerClassName, {
[`Spinner--${mode}`]: !!mode,
[`Spinner--${size}`]: !!size,
[`Spinner--${size}`]: !!size
})}
/>
);

View File

@ -13,13 +13,13 @@ const sizeMap = {
md: 'i-switch-md',
lg: 'i-switch-lg',
middle: 'i-switch-md',
large: 'i-switch-lg',
large: 'i-switch-lg'
};
const levelMap = {
info: 'bg-info',
primary: 'bg-primary',
danger: 'bg-danger',
danger: 'bg-danger'
};
interface SwitchProps {
@ -42,7 +42,7 @@ interface SwitchProps {
export class Switch extends React.PureComponent<SwitchProps, any> {
static defaultProps = {
trueValue: true,
falseValue: false,
falseValue: false
};
constructor(props: SwitchProps) {

View File

@ -5,9 +5,9 @@
*/
import React from 'react';
import { Schema } from '../types';
import {Schema} from '../types';
import Transition, {ENTERED, ENTERING} from 'react-transition-group/Transition';
import { ClassNamesFn, themeable } from '../theme';
import {ClassNamesFn, themeable} from '../theme';
const transitionStyles: {
[propName: string]: string;
@ -28,7 +28,7 @@ export interface TabProps {
mountOnEnter?: boolean;
unmountOnExit?: boolean;
toolbar?: React.ReactNode;
};
}
export interface TabsProps {
mode?: '' | 'line' | 'card' | 'radio';
@ -45,13 +45,13 @@ export interface TabsProps {
}
export class Tabs extends React.Component<TabsProps> {
static defaultProps:Pick<TabsProps, 'mode' | 'contentClassName'> = {
static defaultProps: Pick<TabsProps, 'mode' | 'contentClassName'> = {
mode: '',
contentClassName: ''
};
handleSelect(key: any) {
const { handleSelect } = this.props;
const {handleSelect} = this.props;
handleSelect && handleSelect(key);
}
@ -60,31 +60,29 @@ export class Tabs extends React.Component<TabsProps> {
return;
}
const { classnames: cx, activeKey } = this.props;
const { eventKey, disabled, icon, title, toolbar } = child.props;
const {classnames: cx, activeKey} = this.props;
const {eventKey, disabled, icon, title, toolbar} = child.props;
return (
<li
className={cx(
'Tabs-link',
activeKey === eventKey ? 'is-active' : '',
disabled ? 'is-disabled' : ''
)}
className={cx('Tabs-link', activeKey === eventKey ? 'is-active' : '', disabled ? 'is-disabled' : '')}
key={index}
onClick={() => disabled ? '' : this.handleSelect(eventKey)}
onClick={() => (disabled ? '' : this.handleSelect(eventKey))}
>
<a>{icon ? <i className={icon} /> : null} {title}</a>
<a>
{icon ? <i className={icon} /> : null} {title}
</a>
{React.isValidElement(toolbar) ? toolbar : null}
</li>
);
}
renderTab(child:any, index:number) {
renderTab(child: any, index: number) {
if (!child) {
return;
}
const { activeKey, classnames} = this.props;
const {activeKey, classnames} = this.props;
return React.cloneElement(child, {
...child.props,
@ -95,15 +93,7 @@ export class Tabs extends React.Component<TabsProps> {
}
render() {
const {
classnames: cx,
contentClassName,
className,
mode: dMode,
tabsMode,
children,
additionBtns
} = this.props;
const {classnames: cx, contentClassName, className, mode: dMode, tabsMode, children, additionBtns} = this.props;
if (!Array.isArray(children)) {
return null;
@ -116,21 +106,17 @@ export class Tabs extends React.Component<TabsProps> {
className={cx(
`Tabs`,
{
[`Tabs--${mode}`]: mode,
[`Tabs--${mode}`]: mode
},
className
)}
>
<ul className={cx('Tabs-links')} role="tablist">
{children.map((tab, index) => (
this.renderNav(tab, index)
))}
{children.map((tab, index) => this.renderNav(tab, index))}
{additionBtns}
</ul>
<div
className={cx('Tabs-content', contentClassName)}
>
<div className={cx('Tabs-content', contentClassName)}>
{children.map((child, index) => {
return this.renderTab(child, index);
})}
@ -163,27 +149,30 @@ export class Tab extends React.PureComponent<TabProps> {
unmountOnExit={typeof reload === 'boolean' ? reload : unmountOnExit}
timeout={500}
>
{(status:string) => {
{(status: string) => {
if (status === ENTERING) {
this.contentDom.offsetWidth;
}
return (
<div
ref={this.contentRef}
className={cx && cx(
transitionStyles[status],
activeKey === eventKey ? 'is-active' : '',
'Tabs-pane',
className
)}
className={
cx &&
cx(
transitionStyles[status],
activeKey === eventKey ? 'is-active' : '',
'Tabs-pane',
className
)
}
>
{children}
</div>
)
);
}}
</Transition>
)
);
}
}
export default themeable(Tabs);
export default themeable(Tabs);

View File

@ -26,7 +26,7 @@ export class TitleBar extends React.PureComponent<TitleBarProps, any> {
className: 'bg-light lter b-b',
title: '标题',
titleClassName: 'm-n font-thin h3',
right: false,
right: false
};
render(): JSX.Element {

View File

@ -17,7 +17,7 @@ const fadeStyles: {
[ENTERING]: 'in',
[ENTERED]: '',
[EXITING]: 'out',
[EXITED]: 'hidden',
[EXITED]: 'hidden'
};
let toastRef: any = null;
@ -56,17 +56,17 @@ interface ToastComponentState {
}
export class ToastComponent extends React.Component<ToastComponentProps, ToastComponentState> {
static defaultProps:Pick<ToastComponentProps, 'position' | 'closeButton' | 'timeOut' | 'extendedTimeOut'> = {
static defaultProps: Pick<ToastComponentProps, 'position' | 'closeButton' | 'timeOut' | 'extendedTimeOut'> = {
position: 'top-right',
closeButton: false,
timeOut: 5000,
extendedTimeOut: 3000,
extendedTimeOut: 3000
};
// 当前ToastComponent是否真正render了
hasRendered = false;
state: ToastComponentState = {
items: [],
items: []
};
componentWillMount() {
@ -74,7 +74,7 @@ export class ToastComponent extends React.Component<ToastComponentProps, ToastCo
config = {
closeButton,
timeOut,
extendedTimeOut,
extendedTimeOut
};
}
@ -96,10 +96,10 @@ export class ToastComponent extends React.Component<ToastComponentProps, ToastCo
body: content,
level,
...config,
id: uuid(),
id: uuid()
});
this.setState({
items,
items
});
}
@ -127,7 +127,7 @@ export class ToastComponent extends React.Component<ToastComponentProps, ToastCo
const items = this.state.items.concat();
items.splice(index, 1);
this.setState({
items: items,
items: items
});
}
@ -185,11 +185,11 @@ export class ToastMessage extends React.Component<ToastMessageProps> {
classPrefix: '',
position: 'top-right',
allowHtml: true,
level: 'info',
level: 'info'
};
state = {
visible: false,
visible: false
};
content: React.RefObject<HTMLDivElement>;
@ -210,7 +210,7 @@ export class ToastMessage extends React.Component<ToastMessageProps> {
componentDidMount() {
this.setState({
visible: true,
visible: true
});
}
@ -230,7 +230,7 @@ export class ToastMessage extends React.Component<ToastMessageProps> {
close() {
clearTimeout(this.timer);
this.setState({
visible: false,
visible: false
});
}
@ -277,5 +277,5 @@ export const toast = {
success: (content: string, title?: string, conf?: any) => show(content, title, conf, 'success'),
error: (content: string, title?: string, conf?: any) => show(content, title, conf, 'error'),
info: (content: string, title?: string, conf?: any) => show(content, title, conf, 'info'),
warning: (content: string, title?: string, conf?: any) => show(content, title, conf, 'warning'),
warning: (content: string, title?: string, conf?: any) => show(content, title, conf, 'warning')
};

View File

@ -23,7 +23,7 @@ interface TooltipProps extends React.HTMLProps<HTMLDivElement> {
export class Tooltip extends React.Component<TooltipProps> {
static defaultProps = {
className: '',
className: ''
};
render() {

View File

@ -36,14 +36,14 @@ interface TooltipWrapperState {
show?: boolean;
}
let waitToHide:Function|null = null;
let waitToHide: Function | null = null;
export class TooltipWrapper extends React.Component<TooltipWrapperProps, TooltipWrapperState> {
static defaultProps: Pick<TooltipWrapperProps, 'placement' | 'trigger' | 'rootClose' | 'delay'> = {
placement: 'top',
trigger: ['hover', 'focus'],
rootClose: false,
delay: 200,
delay: 200
};
target: HTMLElement;
@ -64,7 +64,7 @@ export class TooltipWrapper extends React.Component<TooltipWrapperProps, Tooltip
this.handleMouseOut = this.handleMouseOut.bind(this);
this.state = {
show: false,
show: false
};
}
@ -82,14 +82,14 @@ export class TooltipWrapper extends React.Component<TooltipWrapperProps, Tooltip
show() {
this.setState({
show: true,
show: true
});
}
hide() {
waitToHide = null;
this.setState({
show: false,
show: false
});
}
@ -164,7 +164,7 @@ export class TooltipWrapper extends React.Component<TooltipWrapperProps, Tooltip
const childProps: any = {
ref: this.targetRef,
key: 'target',
key: 'target'
};
const triggers = Array.isArray(trigger) ? trigger.concat() : [trigger];
@ -198,13 +198,13 @@ export class TooltipWrapper extends React.Component<TooltipWrapperProps, Tooltip
<Tooltip title={typeof tooltip !== 'string' ? tooltip.title : undefined}>
<Html html={typeof tooltip === 'string' ? tooltip : tooltip.content} />
</Tooltip>
</Overlay>,
</Overlay>
];
}
}
export default themeable(
uncontrollable(TooltipWrapper, {
show: 'onVisibleChange',
show: 'onVisibleChange'
})
);

View File

@ -83,7 +83,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
rootLabel: '顶级',
rootValue: 0,
cascade: false,
selfDisabledAffectChildren: true,
selfDisabledAffectChildren: true
};
componentWillMount() {
@ -102,9 +102,9 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
multiple: props.multiple,
delimiter: props.delimiter,
valueField: props.valueField,
options: props.data,
options: props.data
}),
unfolded: this.syncUnFolded(props),
unfolded: this.syncUnFolded(props)
});
}
@ -118,7 +118,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
multiple: nextProps.multiple,
delimiter: nextProps.delimiter,
valueField: nextProps.valueField,
options: nextProps.data,
options: nextProps.data
});
}
@ -159,15 +159,15 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
this.setState({
unfolded: {
...this.state.unfolded,
[node[this.props.valueField as string]]: !this.state.unfolded[node[this.props.valueField as string]],
},
[node[this.props.valueField as string]]: !this.state.unfolded[node[this.props.valueField as string]]
}
});
}
clearSelect() {
this.setState(
{
value: [],
value: []
},
() => {
const {joinValues, rootValue, onChange} = this.props;
@ -180,7 +180,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
handleSelect(node: any, value?: any) {
this.setState(
{
value: [node],
value: [node]
},
() => {
const {joinValues, valueField, onChange} = this.props;
@ -257,7 +257,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
this.setState(
{
value,
value
},
() => {
const {joinValues, extractValue, valueField, delimiter, onChange} = this.props;
@ -332,11 +332,9 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
let nodeDisabled = !!uncheckable || !!disabled || selfDisabled;
if (
!nodeDisabled
&& (
(maxLength && !selfChecked && this.state.value.length >= maxLength)
|| (minLength && selfChecked && this.state.value.length <= minLength)
)
!nodeDisabled &&
((maxLength && !selfChecked && this.state.value.length >= maxLength) ||
(minLength && selfChecked && this.state.value.length <= minLength))
) {
nodeDisabled = true;
}
@ -369,7 +367,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
<li
key={key}
className={cx(`Tree-item ${itemClassName || ''}`, {
'Tree-item--isLeaf': isLeaf,
'Tree-item--isLeaf': isLeaf
})}
>
<a>
@ -377,7 +375,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
<i
onClick={() => this.toggleUnfolded(item)}
className={cx('Tree-itemArrow', {
'is-folded': !this.state.unfolded[item[valueField]],
'is-folded': !this.state.unfolded[item[valueField]]
})}
/>
) : null}
@ -397,7 +395,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
className={cx('Tree-itemText', {
'is-children-checked': multiple && !cascade && tmpChildrenChecked && !nodeDisabled,
'is-checked': checked,
'is-disabled': nodeDisabled,
'is-disabled': nodeDisabled
})}
onClick={() =>
!nodeDisabled &&
@ -410,7 +408,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
{childrenItems ? (
<ul
className={cx('Tree-sublist', {
'is-folded': !this.state.unfolded[item[valueField]],
'is-folded': !this.state.unfolded[item[valueField]]
})}
>
{childrenItems}
@ -422,7 +420,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
return {
dom: ret,
childrenChecked,
childrenChecked
};
}
@ -444,7 +442,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
<label
className={cx('Tree-itemLabel', {
'is-checked': !value || !value.length,
'is-checked': !value || !value.length
})}
>
<span className={cx('Tree-itemText')} onClick={this.clearSelect}>

View File

@ -26,39 +26,37 @@ import LeftArrowIcon from '../icons/left-arrow.svg';
// @ts-ignore
import RightArrowIcon from '../icons/right-arrow.svg';
// 兼容原来的用法,后续不直接试用。
// @ts-ignore
export const closeIcon = (<CloseIcon className="icon" />);
export const closeIcon = <CloseIcon className="icon" />;
// @ts-ignore
export const unDoIcon = (<UnDoIcon className="icon" />);
export const unDoIcon = <UnDoIcon className="icon" />;
// @ts-ignore
export const reDoIcon = (<ReDoIcon className="icon" />);
export const reDoIcon = <ReDoIcon className="icon" />;
// @ts-ignore
export const enterIcon = (<EnterIcon className="icon" />);
export const enterIcon = <EnterIcon className="icon" />;
// @ts-ignore
export const volumeIcon = (<VolumeIcon className="icon" />);
export const volumeIcon = <VolumeIcon className="icon" />;
// @ts-ignore
export const muteIcon = (<MuteIcon className="icon" />);
export const muteIcon = <MuteIcon className="icon" />;
// @ts-ignore
export const playIcon = (<PlayIcon className="icon" />);
export const playIcon = <PlayIcon className="icon" />;
// @ts-ignore
export const pauseIcon = (<PauseIcon className="icon" />);
export const pauseIcon = <PauseIcon className="icon" />;
// @ts-ignore
export const leftArrowIcon = (<LeftArrowIcon className="icon" />);
export const leftArrowIcon = <LeftArrowIcon className="icon" />;
// @ts-ignore
export const rightArrowIcon = (<RightArrowIcon className="icon" />);
export const rightArrowIcon = <RightArrowIcon className="icon" />;
const iconFactory:{
[propName:string]: React.ReactType<{}>
const iconFactory: {
[propName: string]: React.ReactType<{}>;
} = {};
export function getIcon(key:string) {
export function getIcon(key: string) {
return iconFactory[key];
}
export function registerIcon(key:string, component:React.ReactType<{}>) {
export function registerIcon(key: string, component: React.ReactType<{}>) {
iconFactory[key] = component;
}
@ -76,15 +74,11 @@ registerIcon('right-arrow', RightArrowIcon);
export function Icon({
icon,
...rest
}:{
icon: string
}: {
icon: string;
} & React.ComponentProps<any>) {
const Component = getIcon(icon);
return Component ? (
<Component {...rest} />
) : (
<span className="text-danger"> icon {icon}</span>
);
return Component ? <Component {...rest} /> : <span className="text-danger"> icon {icon}</span>;
}
export {
@ -98,4 +92,4 @@ export {
PauseIcon,
LeftArrowIcon,
RightArrowIcon
}
};

View File

@ -75,5 +75,5 @@ export {
toast,
Tooltip,
TooltipWrapper,
Tree,
Tree
};

View File

@ -1,19 +1,9 @@
import React from "react";
import React from 'react';
import qs from 'qs';
import {
RendererStore,
IRendererStore,
IIRendererStore
} from "./store/index";
import {
getEnv
} from 'mobx-state-tree';
import {
Location, parsePath
} from 'history';
import {
wrapFetcher
} from './utils/api';
import {RendererStore, IRendererStore, IIRendererStore} from './store/index';
import {getEnv} from 'mobx-state-tree';
import {Location, parsePath} from 'history';
import {wrapFetcher} from './utils/api';
import {
createObject,
extendObject,
@ -39,20 +29,24 @@ import {
PlainObject,
RendererData
} from './types';
import { observer } from "mobx-react";
import {observer} from 'mobx-react';
import getExprProperties from './utils/filter-schema';
import hoistNonReactStatic = require('hoist-non-react-statics');
import omit = require('lodash/omit');
import difference = require('lodash/difference');
import isPlainObject = require('lodash/isPlainObject');
import Scoped from './Scoped';
import { getTheme, ThemeInstance, ClassNamesFn, ThemeContext } from "./theme";
import find = require("lodash/find");
import Alert from "./components/Alert2";
import { LazyComponent } from './components';
import {getTheme, ThemeInstance, ClassNamesFn, ThemeContext} from './theme';
import find = require('lodash/find');
import Alert from './components/Alert2';
import {LazyComponent} from './components';
export interface TestFunc {
(path: string, schema?: Schema, resolveRenderer?: (path:string, schema?:Schema, props?:any) => null | RendererConfig): boolean;
(
path: string,
schema?: Schema,
resolveRenderer?: (path: string, schema?: Schema, props?: any) => null | RendererConfig
): boolean;
}
export interface RendererBasicConfig {
@ -67,47 +61,51 @@ export interface RendererBasicConfig {
}
export interface RendererEnv {
fetcher: (api:Api, data?:any, options?: object) => Promise<Payload>;
isCancel: (val:any) => boolean;
notify: (type: "error" | "success", msg: string) => void;
jumpTo: (to:string, action?: Action, ctx?: object) => void;
alert: (msg:string) => void;
confirm: (msg:string, title?: string) => Promise<boolean>;
updateLocation: (location:any, replace?:boolean) => void;
isCurrentUrl: (link:string) => boolean;
rendererResolver?: (path:string, schema:Schema, props:any) => null | RendererConfig;
copy?: (contents:string) => void;
fetcher: (api: Api, data?: any, options?: object) => Promise<Payload>;
isCancel: (val: any) => boolean;
notify: (type: 'error' | 'success', msg: string) => void;
jumpTo: (to: string, action?: Action, ctx?: object) => void;
alert: (msg: string) => void;
confirm: (msg: string, title?: string) => Promise<boolean>;
updateLocation: (location: any, replace?: boolean) => void;
isCurrentUrl: (link: string) => boolean;
rendererResolver?: (path: string, schema: Schema, props: any) => null | RendererConfig;
copy?: (contents: string) => void;
getModalContainer?: () => HTMLElement;
theme: ThemeInstance;
affixOffsetTop: number;
affixOffsetBottom: number;
richTextToken: string;
loadRenderer: (schema:Schema, path:string, reRender:Function) => Promise<React.ReactType> | React.ReactType | JSX.Element | void;
[propName:string]: any;
};
loadRenderer: (
schema: Schema,
path: string,
reRender: Function
) => Promise<React.ReactType> | React.ReactType | JSX.Element | void;
[propName: string]: any;
}
export interface RendererProps {
render: (region: string, node:SchemaNode, props?:any) => JSX.Element;
render: (region: string, node: SchemaNode, props?: any) => JSX.Element;
env: RendererEnv;
classPrefix: string;
classnames: ClassNamesFn;
$path: string; // 当前组件所在的层级信息
store?: IIRendererStore;
data: {
[propName:string]: any;
[propName: string]: any;
};
defaultData?: object;
className?: string;
[propName:string]: any;
[propName: string]: any;
}
export interface renderChildProps extends Partial<RendererProps> {
env: RendererEnv;
};
}
export type RendererComponent = React.ComponentType<RendererProps> & {
propsList?: Array<string>;
}
};
export interface RendererConfig extends RendererBasicConfig {
component: RendererComponent;
@ -115,72 +113,74 @@ export interface RendererConfig extends RendererBasicConfig {
}
export interface RenderSchemaFilter {
(schema:Schema, renderer: RendererConfig, props?:object): SchemaNode;
};
(schema: Schema, renderer: RendererConfig, props?: object): SchemaNode;
}
export interface RootRenderProps {
location?: Location;
theme?: string;
[propName:string]: any;
};
[propName: string]: any;
}
export interface RenderOptions {
session?: string,
fetcher?: (config: fetcherConfig) => Promise<fetcherResult>,
isCancel?: (value:any) => boolean;
notify?: (type: "error" | "success", msg: string) => void,
jumpTo?: (to:string) => void,
alert?: (msg:string) => void,
confirm?: (msg:string, title?: string) => boolean | Promise<boolean>;
rendererResolver?: (path:string, schema:Schema, props:any) => null | RendererConfig;
copy?: (contents:string) => void;
session?: string;
fetcher?: (config: fetcherConfig) => Promise<fetcherResult>;
isCancel?: (value: any) => boolean;
notify?: (type: 'error' | 'success', msg: string) => void;
jumpTo?: (to: string) => void;
alert?: (msg: string) => void;
confirm?: (msg: string, title?: string) => boolean | Promise<boolean>;
rendererResolver?: (path: string, schema: Schema, props: any) => null | RendererConfig;
copy?: (contents: string) => void;
getModalContainer?: () => HTMLElement;
loadRenderer?: (schema:Schema, path: string, reRender:Function) => Promise<React.ReactType> | React.ReactType | JSX.Element | void;
loadRenderer?: (
schema: Schema,
path: string,
reRender: Function
) => Promise<React.ReactType> | React.ReactType | JSX.Element | void;
affixOffsetTop?: number;
affixOffsetBottom?: number;
richTextToken?: string;
[propName: string]: any
[propName: string]: any;
}
export interface fetcherConfig {
export interface fetcherConfig {
url: string;
method: 'get' | 'post' | 'put' | 'patch' | 'delete';
data?: any;
config?: any;
};
}
export type ReactElement = React.ReactNode[] | JSX.Element | null | false;
const renderers:Array<RendererConfig> = [];
const rendererNames:Array<string> = [];
const schemaFilters:Array<RenderSchemaFilter> = [];
const renderers: Array<RendererConfig> = [];
const rendererNames: Array<string> = [];
const schemaFilters: Array<RenderSchemaFilter> = [];
let anonymousIndex = 1;
export function addSchemaFilter(fn:RenderSchemaFilter) {
export function addSchemaFilter(fn: RenderSchemaFilter) {
schemaFilters.push(fn);
}
export function filterSchema(schema:Schema, render:RendererConfig, props?:any) {
export function filterSchema(schema: Schema, render: RendererConfig, props?: any) {
return schemaFilters.reduce((schema, filter) => filter(schema, render, props), schema) as Schema;
}
export function Renderer(config:RendererBasicConfig) {
return function<T extends RendererComponent>(component:T):T {
export function Renderer(config: RendererBasicConfig) {
return function<T extends RendererComponent>(component: T): T {
const renderer = registerRenderer({
...config,
component: component
});
return renderer.component as T;
}
};
}
export function registerRenderer(config:RendererConfig):RendererConfig {
export function registerRenderer(config: RendererConfig): RendererConfig {
if (!config.test) {
throw new TypeError("config.test is required");
throw new TypeError('config.test is required');
} else if (!config.component) {
throw new TypeError("config.component is required");
throw new TypeError('config.component is required');
}
config.weight = config.weight || 0;
@ -208,45 +208,47 @@ export function registerRenderer(config:RendererConfig):RendererConfig {
return config;
}
export function unRegisterRenderer(config:RendererConfig | string) {
let idx = typeof config === 'string' ? findIndex(renderers, item => item.name === config) : renderers.indexOf(config);
export function unRegisterRenderer(config: RendererConfig | string) {
let idx =
typeof config === 'string' ? findIndex(renderers, item => item.name === config) : renderers.indexOf(config);
~idx && renderers.splice(idx, 1);
// 清空渲染器定位缓存
cache = {};
}
export function renderChildren(prefix: string, node: SchemaNode, props:renderChildProps):ReactElement {
export function renderChildren(prefix: string, node: SchemaNode, props: renderChildProps): ReactElement {
if (Array.isArray(node)) {
return node.map((node, index) => renderChild(`${prefix}/${index}`, node, {
...props,
key: `${props.key ? `${props.key}-` : ''}${index}`
}));
return node.map((node, index) =>
renderChild(`${prefix}/${index}`, node, {
...props,
key: `${props.key ? `${props.key}-` : ''}${index}`
})
);
}
return renderChild(prefix, node, props);
}
export function renderChild(prefix:string, node:SchemaNode, props:renderChildProps):ReactElement {
export function renderChild(prefix: string, node: SchemaNode, props: renderChildProps): ReactElement {
if (Array.isArray(node)) {
return renderChildren(prefix, node, props);
}
const typeofnode = typeof node;
let schema:Schema = typeofnode === 'string' || typeofnode === 'number' ? {type: 'tpl', tpl: String(node)} : node as Schema;
let schema: Schema =
typeofnode === 'string' || typeofnode === 'number' ? {type: 'tpl', tpl: String(node)} : (node as Schema);
const detectData = schema.detectField === '&' ? props : props[schema.detectField || 'data'];
const exprProps = detectData ? getExprProperties(schema, detectData) : null;
if (
exprProps
&& (
exprProps.hidden
|| exprProps.visible === false
|| schema.hidden
|| schema.visible === false
|| props.hidden
|| props.visible === false
)
exprProps &&
(exprProps.hidden ||
exprProps.visible === false ||
schema.hidden ||
schema.visible === false ||
props.hidden ||
props.visible === false)
) {
return null;
}
@ -256,10 +258,10 @@ export function renderChild(prefix:string, node:SchemaNode, props:renderChildPro
{...props}
{...exprProps}
schema={schema}
$path={`${prefix ? `${prefix}/` : ''}${schema && schema.type || ''}`}
$path={`${prefix ? `${prefix}/` : ''}${(schema && schema.type) || ''}`}
/>
);
};
}
export interface RootRendererProps {
schema: SchemaNode;
@ -267,8 +269,8 @@ export interface RootRendererProps {
env: RendererEnv;
theme: string;
pathPrefix?: string;
[propName:string]: any;
};
[propName: string]: any;
}
const RootStoreContext = React.createContext<IRendererStore>(undefined as any);
@ -276,9 +278,9 @@ export class RootRenderer extends React.Component<RootRendererProps> {
state = {
error: null,
errorInfo: null
}
};
componentDidCatch(error:any, errorInfo:any) {
componentDidCatch(error: any, errorInfo: any) {
this.setState({
error: error,
errorInfo: errorInfo
@ -286,7 +288,7 @@ export class RootRenderer extends React.Component<RootRendererProps> {
}
@autobind
resolveDefinitions(name:string) {
resolveDefinitions(name: string) {
const definitions = (this.props.schema as Schema).definitions;
if (!name || isEmpty(definitions)) {
return {};
@ -299,42 +301,48 @@ export class RootRenderer extends React.Component<RootRendererProps> {
if (errorInfo) {
return errorRenderer(error, errorInfo);
}
const {
schema,
rootStore,
env,
pathPrefix,
location,
data,
...rest
} = this.props;
const {schema, rootStore, env, pathPrefix, location, data, ...rest} = this.props;
const theme = env.theme;
const query = location && location.query
|| location && location.search && qs.parse(location.search.substring(1))
|| window.location.search && qs.parse(window.location.search.substring(1));
const query =
(location && location.query) ||
(location && location.search && qs.parse(location.search.substring(1))) ||
(window.location.search && qs.parse(window.location.search.substring(1)));
const finalData = query ? createObject({
...(data && data.__super ? data.__super: null),
...query,
query
}, data) : data;
const finalData = query
? createObject(
{
...(data && data.__super ? data.__super : null),
...query,
query
},
data
)
: data;
return (
<RootStoreContext.Provider value={rootStore}>
<ThemeContext.Provider value={this.props.theme || 'default'}>
{renderChild(pathPrefix || '', isPlainObject(schema) ? {
type: 'page',
...(schema as Schema)
} : schema, {
...rest,
resolveDefinitions: this.resolveDefinitions,
location: location,
data: finalData,
env,
classnames: theme.classnames,
classPrefix: theme.classPrefix
}) as JSX.Element}
{
renderChild(
pathPrefix || '',
isPlainObject(schema)
? {
type: 'page',
...(schema as Schema)
}
: schema,
{
...rest,
resolveDefinitions: this.resolveDefinitions,
location: location,
data: finalData,
env,
classnames: theme.classnames,
classPrefix: theme.classPrefix
}
) as JSX.Element
}
</ThemeContext.Provider>
</RootStoreContext.Provider>
);
@ -347,22 +355,33 @@ interface SchemaRendererProps extends Partial<RendererProps> {
schema: Schema;
$path: string;
env: RendererEnv;
};
}
const defaultOmitList = [
'type', 'name', '$ref', 'className', 'data', 'children',
'ref', 'visible', 'visibleOn', 'hidden',
'hiddenOn', 'disabled', 'disabledOn',
'children', 'detectField'
'type',
'name',
'$ref',
'className',
'data',
'children',
'ref',
'visible',
'visibleOn',
'hidden',
'hiddenOn',
'disabled',
'disabledOn',
'children',
'detectField'
];
class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
static displayName:string = 'Renderer';
static displayName: string = 'Renderer';
renderer:RendererConfig | null;
renderer: RendererConfig | null;
ref: any;
constructor(props:SchemaRendererProps) {
constructor(props: SchemaRendererProps) {
super(props);
this.refFn = this.refFn.bind(this);
this.renderChild = this.renderChild.bind(this);
@ -373,34 +392,28 @@ class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
this.resolveRenderer(this.props);
}
componentWillReceiveProps(nextProps:SchemaRendererProps) {
componentWillReceiveProps(nextProps: SchemaRendererProps) {
const props = this.props;
if (
props.schema.type !== nextProps.schema.type ||
props.schema.$$id !== nextProps.schema.$$id
) {
if (props.schema.type !== nextProps.schema.type || props.schema.$$id !== nextProps.schema.$$id) {
this.resolveRenderer(nextProps);
}
}
// 限制:只有 schema 除外的 props 变化,或者 schema 里面的某个成员值发生变化才更新。
shouldComponentUpdate(nextProps:SchemaRendererProps) {
shouldComponentUpdate(nextProps: SchemaRendererProps) {
const props = this.props;
const list:Array<string> = difference(Object.keys(nextProps), ['schema']);
const list: Array<string> = difference(Object.keys(nextProps), ['schema']);
if (
difference(Object.keys(props), ['schema']).length !== list.length
|| anyChanged(list, this.props, nextProps)
difference(Object.keys(props), ['schema']).length !== list.length ||
anyChanged(list, this.props, nextProps)
) {
return true;
} else {
const list:Array<string> = Object.keys(nextProps.schema);
const list: Array<string> = Object.keys(nextProps.schema);
if (
Object.keys(props.schema).length !== list.length
|| anyChanged(list, props.schema, nextProps.schema)
) {
if (Object.keys(props.schema).length !== list.length || anyChanged(list, props.schema, nextProps.schema)) {
return true;
}
}
@ -408,7 +421,7 @@ class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
return false;
}
resolveRenderer(props:SchemaRendererProps):any {
resolveRenderer(props: SchemaRendererProps): any {
let schema = props.schema;
let path = props.$path;
const rendererResolver = props.env.rendererResolver || resolveRenderer;
@ -436,20 +449,19 @@ class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
return this.ref;
}
refFn(ref:any) {
refFn(ref: any) {
this.ref = ref;
}
renderChild(region: string, node?:SchemaNode, subProps: {
data?: object;
[propName: string]: any;
} = {}) {
let {
schema,
$path,
env,
...rest
} = this.props;
renderChild(
region: string,
node?: SchemaNode,
subProps: {
data?: object;
[propName: string]: any;
} = {}
) {
let {schema, $path, env, ...rest} = this.props;
const omitList = defaultOmitList.concat();
if (this.renderer) {
@ -470,12 +482,8 @@ class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
this.forceUpdate();
}
render():JSX.Element | null {
let {
$path,
schema,
...rest
} = this.props;
render(): JSX.Element | null {
let {$path, schema, ...rest} = this.props;
if (schema.$ref) {
schema = this.resolveRenderer(this.props);
@ -486,25 +494,27 @@ class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
if (Array.isArray(schema)) {
return renderChildren($path, schema, rest) as JSX.Element;
} else if (schema.children) {
return React.isValidElement(schema.children) ? schema.children : React.createElement(schema.children as any, {
...rest,
$path: $path,
render: this.renderChild
});
return React.isValidElement(schema.children)
? schema.children
: React.createElement(schema.children as any, {
...rest,
$path: $path,
render: this.renderChild
});
} else if (!this.renderer) {
return (
<LazyComponent
{...rest}
getComponent={async () => {
const result = await rest.env.loadRenderer(schema, $path, this.reRender);
if (result && typeof result === "function") {
if (result && typeof result === 'function') {
return result;
} else if (result && React.isValidElement(result)) {
return () => result;
}
this.reRender();
return () => loadRenderer(schema, $path)
return () => loadRenderer(schema, $path);
}}
$path={$path}
retry={this.reRender}
@ -514,10 +524,7 @@ class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
const renderer = this.renderer as RendererConfig;
schema = filterSchema(schema, renderer, rest);
const {
data: defaultData,
...restSchema
} = schema;
const {data: defaultData, ...restSchema} = schema;
const Component = renderer.component;
return (
@ -534,13 +541,9 @@ class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
}
}
export function HocStoreFactory(renderer:{
storeType: string;
extendsData?: boolean;
}):any {
return function <T extends React.ComponentType<RendererProps>>(Component:T) {
type Props = Omit<RendererProps, "store" | "data" | "dataUpdatedAt" | "scope"> & {
export function HocStoreFactory(renderer: {storeType: string; extendsData?: boolean}): any {
return function<T extends React.ComponentType<RendererProps>>(Component: T) {
type Props = Omit<RendererProps, 'store' | 'data' | 'dataUpdatedAt' | 'scope'> & {
store?: IIRendererStore;
data?: RendererData;
scope?: RendererData;
@ -551,23 +554,23 @@ export function HocStoreFactory(renderer:{
static displayName = `WithStore(${Component.displayName || Component.name})`;
static ComposedComponent = Component;
static contextType = RootStoreContext;
store:IIRendererStore;
store: IIRendererStore;
context!: React.ContextType<typeof RootStoreContext>;
ref:any;
ref: any;
getWrappedInstance() {
return this.ref;
}
refFn(ref:any) {
refFn(ref: any) {
this.ref = ref;
}
formatData(data:any):object {
formatData(data: any): object {
if (Array.isArray(data)) {
return {
items: data
}
};
}
return data as object;
@ -578,28 +581,34 @@ export function HocStoreFactory(renderer:{
this.renderChild = this.renderChild.bind(this);
this.refFn = this.refFn.bind(this);
const store = this.store = rootStore.addStore({
const store = (this.store = rootStore.addStore({
id: guid(),
path: this.props.$path,
storeType: renderer.storeType,
parentId: this.props.store ? this.props.store.id : ''
});
}));
if (renderer.extendsData === false) {
store.initData(createObject((this.props.data as any) ? (this.props.data as any).__super : null, {
...this.formatData(this.props.defaultData),
...this.formatData(this.props.data)
}));
} else if (this.props.scope || this.props.data && (this.props.data as any).__super) {
if (this.props.store && this.props.data === this.props.store.data) {
store.initData(createObject(this.props.store.data, {
...this.formatData(this.props.defaultData)
}))
} else {
store.initData(createObject((this.props.data as any).__super || this.props.scope, {
store.initData(
createObject((this.props.data as any) ? (this.props.data as any).__super : null, {
...this.formatData(this.props.defaultData),
...this.formatData(this.props.data)
}))
})
);
} else if (this.props.scope || (this.props.data && (this.props.data as any).__super)) {
if (this.props.store && this.props.data === this.props.store.data) {
store.initData(
createObject(this.props.store.data, {
...this.formatData(this.props.defaultData)
})
);
} else {
store.initData(
createObject((this.props.data as any).__super || this.props.scope, {
...this.formatData(this.props.defaultData),
...this.formatData(this.props.data)
})
);
}
} else {
store.initData({
@ -609,26 +618,33 @@ export function HocStoreFactory(renderer:{
}
}
componentWillReceiveProps(nextProps:RendererProps) {
componentWillReceiveProps(nextProps: RendererProps) {
const props = this.props;
const store = this.store;
if (renderer.extendsData === false) {
(
props.defaultData !== nextProps.defaultData
|| isObjectShallowModified(props.data, nextProps.data)
(props.defaultData !== nextProps.defaultData ||
isObjectShallowModified(props.data, nextProps.data) ||
// CRUD 中 toolbar 里面的 data 是空对象,但是 __super 会不一样
|| nextProps.data && props.data && nextProps.data.__super !== props.data.__super
)
&& store.initData(extendObject(nextProps.data, {
...store.hasRemoteData ? store.data : null, // todo 只保留 remote 数据
...this.formatData(nextProps.defaultData),
...this.formatData(nextProps.data)
}));
(nextProps.data && props.data && nextProps.data.__super !== props.data.__super)) &&
store.initData(
extendObject(nextProps.data, {
...(store.hasRemoteData ? store.data : null), // todo 只保留 remote 数据
...this.formatData(nextProps.defaultData),
...this.formatData(nextProps.data)
})
);
} else if (isObjectShallowModified(props.data, nextProps.data)) {
if (nextProps.store && nextProps.store.data === nextProps.data) {
const newData = createObject(nextProps.store.data, syncDataFromSuper(store.data,
nextProps.store.data, props.scope, nextProps.dataUpdatedAt !== props.dataUpdatedAt));
const newData = createObject(
nextProps.store.data,
syncDataFromSuper(
store.data,
nextProps.store.data,
props.scope,
nextProps.dataUpdatedAt !== props.dataUpdatedAt
)
);
// todo fix: dialog 种数据从孩子 form 同步过来后,会走这个逻辑让 form 更新 data会导致里面的 __prev 丢失。
store.initData(newData);
@ -637,18 +653,26 @@ export function HocStoreFactory(renderer:{
} else {
store.initData(createObject(nextProps.scope, nextProps.data));
}
} else if ((!nextProps.store || nextProps.data !== nextProps.store.data) && nextProps.data && nextProps.data.__super) {
} else if (
(!nextProps.store || nextProps.data !== nextProps.store.data) &&
nextProps.data &&
nextProps.data.__super
) {
// 这个用法很少,当 data.__super 值发生变化时,更新 store.data
(!props.data || isObjectShallowModified(nextProps.data.__super, props.data.__super, false))
&& store.initData(createObject(nextProps.data.__super, {
(!props.data || isObjectShallowModified(nextProps.data.__super, props.data.__super, false)) &&
store.initData(
createObject(nextProps.data.__super, {
...nextProps.data,
...store.data
})
);
} else if (nextProps.scope !== props.scope) {
store.initData(
createObject(nextProps.scope, {
...nextProps.data,
...store.data
}));
} else if (nextProps.scope !== props.scope) {
store.initData(createObject(nextProps.scope, {
...nextProps.data,
...store.data
}));
})
);
}
}
@ -659,13 +683,15 @@ export function HocStoreFactory(renderer:{
delete this.store;
}
renderChild(region: string, node:SchemaNode, subProps: {
data?: object;
[propName: string]: any;
} = {}) {
let {
render,
} = this.props;
renderChild(
region: string,
node: SchemaNode,
subProps: {
data?: object;
[propName: string]: any;
} = {}
) {
let {render} = this.props;
return render(region, node, {
data: this.store.data,
@ -677,13 +703,10 @@ export function HocStoreFactory(renderer:{
}
render() {
const {
detectField,
...rest
} = this.props;
const {detectField, ...rest} = this.props;
let exprProps:any = {};
if (!detectField || detectField === "data") {
let exprProps: any = {};
if (!detectField || detectField === 'data') {
exprProps = getExprProperties(rest, this.store.data);
if (exprProps.hidden || exprProps.visible === false) {
@ -693,7 +716,9 @@ export function HocStoreFactory(renderer:{
return (
<Component
{...rest as any /* todo */}
{
...rest as any /* todo */
}
{...exprProps}
ref={this.refFn}
data={this.store.data}
@ -708,30 +733,33 @@ export function HocStoreFactory(renderer:{
hoistNonReactStatic(StoreFactory, Component);
return StoreFactory;
}
};
}
function loadRenderer(schema:Schema, path:string) {
function loadRenderer(schema: Schema, path: string) {
return (
<Alert level="danger">
<p>Error: </p>
<p>Path: {path}</p>
<pre><code>{JSON.stringify(schema, null, 2)}</code></pre>
<pre>
<code>{JSON.stringify(schema, null, 2)}</code>
</pre>
</Alert>
);
}
function errorRenderer(error:any, errorInfo:any) {
function errorRenderer(error: any, errorInfo: any) {
return (
<Alert level="danger">
<p>{error && error.toString()}</p>
<pre><code>{errorInfo.componentStack}</code></pre>
<pre>
<code>{errorInfo.componentStack}</code>
</pre>
</Alert>
)
);
}
const defaultOptions:RenderOptions = {
const defaultOptions: RenderOptions = {
session: 'global',
affixOffsetTop: 50,
affixOffsetBottom: 0,
@ -741,46 +769,62 @@ const defaultOptions:RenderOptions = {
return Promise.reject('fetcher is required');
},
isCancel() {
console.error('Please implements this. see https://baidu.github.io/amis/docs/getting-started#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8');
console.error(
'Please implements this. see https://baidu.github.io/amis/docs/getting-started#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8'
);
return false;
},
alert(msg:string) {
alert(msg: string) {
alert(msg);
},
updateLocation() {
console.error('Please implements this. see https://baidu.github.io/amis/docs/getting-started#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8');
console.error(
'Please implements this. see https://baidu.github.io/amis/docs/getting-started#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8'
);
},
confirm(msg:string) {
return confirm(msg)
confirm(msg: string) {
return confirm(msg);
},
notify(msg) {
alert(msg);
},
jumpTo() {
console.error('Please implements this. see https://baidu.github.io/amis/docs/getting-started#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8');
console.error(
'Please implements this. see https://baidu.github.io/amis/docs/getting-started#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8'
);
},
isCurrentUrl() {
return false;
},
copy(contents:string) {
console.error('copy contents', contents)
copy(contents: string) {
console.error('copy contents', contents);
},
rendererResolver: resolveRenderer
};
let stores:{
[propName:string]: IRendererStore
let stores: {
[propName: string]: IRendererStore;
} = {};
export function render(schema:SchemaNode, props:RootRenderProps = {}, options:RenderOptions = {}, pathPrefix:string = ''):JSX.Element {
export function render(
schema: SchemaNode,
props: RootRenderProps = {},
options: RenderOptions = {},
pathPrefix: string = ''
): JSX.Element {
options = {
...defaultOptions,
...options
};
let store = stores[options.session || 'global'] || (stores[options.session || 'global'] = RendererStore.create({}, {
...options,
fetcher: options.fetcher ? wrapFetcher(options.fetcher) : defaultOptions.fetcher,
confirm: options.confirm ? promisify(options.confirm) : defaultOptions.confirm,
}));
let store =
stores[options.session || 'global'] ||
(stores[options.session || 'global'] = RendererStore.create(
{},
{
...options,
fetcher: options.fetcher ? wrapFetcher(options.fetcher) : defaultOptions.fetcher,
confirm: options.confirm ? promisify(options.confirm) : defaultOptions.confirm
}
));
(window as any).amisStore = store; // 为了方便 debug.
const env = getEnv(store);
@ -799,7 +843,7 @@ export function render(schema:SchemaNode, props:RootRenderProps = {}, options:Re
);
}
export function clearStoresCache(sessions?:Array<string>) {
export function clearStoresCache(sessions?: Array<string>) {
if (Array.isArray(sessions) && sessions.length) {
sessions.forEach(key => delete stores[key]);
} else {
@ -807,20 +851,20 @@ export function clearStoresCache(sessions?:Array<string>) {
}
}
let cache:{[propName: string]: RendererConfig} = {};
export function resolveRenderer(path:string, schema?:Schema, props?:any): null | RendererConfig {
let cache: {[propName: string]: RendererConfig} = {};
export function resolveRenderer(path: string, schema?: Schema, props?: any): null | RendererConfig {
if (cache[path]) {
return cache[path];
} else if (path && path.length > 1024) {
throw new Error('Path太长是不是死循环了');
}
let renderer:null | RendererConfig = null;
let renderer: null | RendererConfig = null;
renderers.some(item => {
let matched = false;
if (typeof item.test === "function") {
if (typeof item.test === 'function') {
matched = item.test(path, schema, resolveRenderer);
} else if (item.test instanceof RegExp) {
matched = item.test.test(path);
@ -834,22 +878,22 @@ export function resolveRenderer(path:string, schema?:Schema, props?:any): null |
});
// 只能缓存纯正则表达式的后者方法中没有用到第二个参数的,因为自定义 test 函数的有可能依赖 schema 的结果
if (renderer !== null &&
(
(renderer as RendererConfig).test instanceof RegExp
|| typeof (renderer as RendererConfig).test === 'function' && ((renderer as RendererConfig).test as Function).length < 2
)) {
if (
renderer !== null &&
((renderer as RendererConfig).test instanceof RegExp ||
(typeof (renderer as RendererConfig).test === 'function' &&
((renderer as RendererConfig).test as Function).length < 2))
) {
cache[path] = renderer;
}
return renderer;
}
export function getRenderers() {
return renderers.concat();
}
export function getRendererByName(name:string) {
export function getRendererByName(name: string) {
return find(renderers, item => item.name === name);
}

View File

@ -54,7 +54,7 @@ export interface ActionProps {
level?: 'info' | 'success' | 'warning' | 'danger' | 'link';
onAction?: (e: React.MouseEvent<any> | void | null, action: object) => void;
isCurrentUrl?: (link: string) => boolean;
onClick?: (e: React.MouseEvent<any>, props:any) => void;
onClick?: (e: React.MouseEvent<any>, props: any) => void;
primary?: boolean;
activeClassName: string;
componentClass: React.ReactType;
@ -80,7 +80,7 @@ export class Action extends React.Component<ActionProps> {
type: 'button',
componentClass: 'button',
tooltipPlacement: 'bottom',
activeClassName: 'is-active',
activeClassName: 'is-active'
};
dom: any;
@ -89,7 +89,7 @@ export class Action extends React.Component<ActionProps> {
handleAction(e: React.MouseEvent<any>) {
const {onAction, onClick, disabled} = this.props;
const result:any = onClick && onClick(e, this.props);
const result: any = onClick && onClick(e, this.props);
if (disabled || e.isDefaultPrevented() || result === false || !onAction) {
return;
@ -125,7 +125,7 @@ export class Action extends React.Component<ActionProps> {
active,
activeLevel,
tooltipContainer,
classnames: cx,
classnames: cx
} = this.props;
let isActive = !!active;
@ -138,7 +138,7 @@ export class Action extends React.Component<ActionProps> {
<a
className={cx(className, {
[activeClassName || 'is-active']: isActive,
'is-disabled': disabled,
'is-disabled': disabled
})}
onClick={this.handleAction}
>
@ -148,7 +148,7 @@ export class Action extends React.Component<ActionProps> {
) : (
<Button
className={cx(className, {
[activeClassName || 'is-active']: isActive,
[activeClassName || 'is-active']: isActive
})}
size={size}
level={activeLevel && isActive ? activeLevel : level || (primary ? 'primary' : undefined)}
@ -174,7 +174,7 @@ export default themeable(Action);
@Renderer({
test: /(^|\/)action$/,
name: 'action',
name: 'action'
})
export class ActionRenderer extends React.Component<
RendererProps &
@ -219,18 +219,18 @@ export class ActionRenderer extends React.Component<
@Renderer({
test: /(^|\/)button$/,
name: 'button',
name: 'button'
})
export class ButtonRenderer extends ActionRenderer {}
@Renderer({
test: /(^|\/)submit$/,
name: 'submit',
name: 'submit'
})
export class SubmitRenderer extends ActionRenderer {}
@Renderer({
test: /(^|\/)reset$/,
name: 'reset',
name: 'reset'
})
export class ResetRenderer extends ActionRenderer {}

View File

@ -4,7 +4,7 @@ import Alert, {AlertProps} from '../components/Alert2';
@Renderer({
test: /(^|\/)alert$/,
name: 'alert',
name: 'alert'
})
export class TplRenderer extends React.Component<AlertProps & RendererProps> {
render() {

View File

@ -50,7 +50,11 @@ export class Audio extends React.Component<AudioProps, AudioState> {
};
state: AudioState = {
src: this.props.value || (this.props.src ? filter(this.props.src, this.props.data) : '') || resolveVariable(this.props.name, this.props.data) || '',
src:
this.props.value ||
(this.props.src ? filter(this.props.src, this.props.data) : '') ||
resolveVariable(this.props.name, this.props.data) ||
'',
isReady: false,
muted: false,
playing: false,
@ -61,7 +65,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
loaded: 0,
playbackRate: 1.0,
showHandlePlaybackRate: false,
showHandleVolume: false,
showHandleVolume: false
};
componentWillUnmount() {
@ -74,26 +78,29 @@ export class Audio extends React.Component<AudioProps, AudioState> {
const playing = autoPlay ? true : false;
this.setState(
{
playing: playing,
playing: playing
},
this.progress
);
}
componentWillReceiveProps(nextProps:AudioProps) {
componentWillReceiveProps(nextProps: AudioProps) {
const props = this.props;
if (
props.value !== nextProps.value ||
filter(props.src as string, props.data) !== filter(nextProps.src as string, nextProps.data)
) {
this.setState({
src: nextProps.value || filter(nextProps.src as string, nextProps.data),
playing: false
}, () => {
this.audio.load();
this.progress();
});
this.setState(
{
src: nextProps.value || filter(nextProps.src as string, nextProps.data),
playing: false
},
() => {
this.audio.load();
this.progress();
}
);
}
}
@ -108,7 +115,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
playing = played != 1 && playing ? true : false;
this.setState({
played,
playing,
playing
});
this.progressTimeout = setTimeout(this.progress, this.props.progressInterval / this.state.playbackRate);
}
@ -122,7 +129,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
@autobind
load() {
this.setState({
isReady: true,
isReady: true
});
}
@ -131,7 +138,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
this.audio.playbackRate = rate;
this.setState({
playbackRate: rate,
showHandlePlaybackRate: false,
showHandlePlaybackRate: false
});
}
@ -145,7 +152,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
this.audio.muted = !muted;
this.setState({
muted: !muted,
volume: curVolume,
volume: curVolume
});
}
@ -157,7 +164,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
let playing = this.state.playing;
playing ? this.audio.pause() : this.audio.play();
this.setState({
playing: !playing,
playing: !playing
});
}
@ -227,7 +234,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
playing = played < 1 || loop ? playing : false;
this.setState({
playing: playing,
seeking: false,
seeking: false
});
}
@ -240,7 +247,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
this.audio.volume = volume;
this.setState({
volume: volume,
prevVolume: volume,
prevVolume: volume
});
}
@ -267,7 +274,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
return;
}
this.setState({
showHandlePlaybackRate: !this.state.showHandlePlaybackRate,
showHandlePlaybackRate: !this.state.showHandlePlaybackRate
});
}
@ -277,42 +284,33 @@ export class Audio extends React.Component<AudioProps, AudioState> {
return;
}
this.setState({
showHandleVolume: type,
showHandleVolume: type
});
}
renderRates() {
const {
rates,
classnames: cx
} = this.props;
const {
showHandlePlaybackRate,
playbackRate
} = this.state;
const {rates, classnames: cx} = this.props;
const {showHandlePlaybackRate, playbackRate} = this.state;
return (
rates && rates.length ?
showHandlePlaybackRate ? (
<div className={cx('Audio-rateControl')}>
{rates.map((rate, index) =>
<div
key={index}
className={cx('Audio-rateControlItem')}
onClick={() => this.handlePlaybackRate(rate)}>
x{rate.toFixed(1)}
</div>
)}
</div>
) : (
<div
className={cx('Audio-rates')}
onClick={this.toggleHandlePlaybackRate}>
x{playbackRate.toFixed(1)}
</div>
)
: null
)
return rates && rates.length ? (
showHandlePlaybackRate ? (
<div className={cx('Audio-rateControl')}>
{rates.map((rate, index) => (
<div
key={index}
className={cx('Audio-rateControlItem')}
onClick={() => this.handlePlaybackRate(rate)}
>
x{rate.toFixed(1)}
</div>
))}
</div>
) : (
<div className={cx('Audio-rates')} onClick={this.toggleHandlePlaybackRate}>
x{playbackRate.toFixed(1)}
</div>
)
) : null;
}
renderPlay() {
@ -320,12 +318,10 @@ export class Audio extends React.Component<AudioProps, AudioState> {
const {playing} = this.state;
return (
<div
className={cx('Audio-play')}
onClick={this.handlePlaying}>
<div className={cx('Audio-play')} onClick={this.handlePlaying}>
{playing ? <Icon icon="pause" className="icon" /> : <Icon icon="play" className="icon" />}
</div>
)
);
}
renderTime() {
@ -335,7 +331,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
<div className={cx('Audio-times')}>
{this.getCurrentTime()} / {this.getDuration()}
</div>
)
);
}
renderProcess() {
@ -346,56 +342,38 @@ export class Audio extends React.Component<AudioProps, AudioState> {
<div className={cx('Audio-process')}>
<input
type="range"
min={0} max={1} step="any"
min={0}
max={1}
step="any"
value={played || 0}
onMouseDown={this.onSeekMouseDown}
onChange={this.onSeekChange}
onMouseUp={this.onSeekMouseUp} />
onMouseUp={this.onSeekMouseUp}
/>
</div>
)
);
}
renderVolume() {
const {classnames: cx} = this.props;
const {
volume,
showHandleVolume
} = this.state;
const {volume, showHandleVolume} = this.state;
return (
showHandleVolume ? (
<div
className={cx('Audio-volumeControl')}
onMouseLeave={() => this.toggleHandleVolume(false)}>
<div
className={cx('Audio-volumeControlIcon')}
onClick={this.handleMute}>
{volume > 0 ? <Icon icon="volume" className="icon" /> : <Icon icon="mute" className="icon" />}
</div>
<input
type='range' min={0} max={1} step='any'
value={volume}
onChange={this.setVolume} />
</div>
) : (
<div
className={cx('Audio-volume')}
onMouseEnter={() => this.toggleHandleVolume(true)}>
return showHandleVolume ? (
<div className={cx('Audio-volumeControl')} onMouseLeave={() => this.toggleHandleVolume(false)}>
<div className={cx('Audio-volumeControlIcon')} onClick={this.handleMute}>
{volume > 0 ? <Icon icon="volume" className="icon" /> : <Icon icon="mute" className="icon" />}
</div>
)
)
<input type="range" min={0} max={1} step="any" value={volume} onChange={this.setVolume} />
</div>
) : (
<div className={cx('Audio-volume')} onMouseEnter={() => this.toggleHandleVolume(true)}>
{volume > 0 ? <Icon icon="volume" className="icon" /> : <Icon icon="mute" className="icon" />}
</div>
);
}
render() {
const {
className,
inline,
autoPlay,
loop,
controls,
classnames: cx
} = this.props;
const {className, inline, autoPlay, loop, controls, classnames: cx} = this.props;
const {muted, src} = this.state;
return (
@ -407,19 +385,23 @@ export class Audio extends React.Component<AudioProps, AudioState> {
autoPlay={autoPlay}
controls
muted={muted}
loop={loop}>
loop={loop}
>
<source src={src} />
</audio>
<div className={cx('Audio-controls')}>
{controls && controls.map((control:string, index:number) => {
control = 'render' + upperFirst(control);
const method:'renderRates'|'renderPlay'|'renderTime'|'renderProcess'|'renderVolume'|'render' = control as any;
return (
<React.Fragment key={index}>
{this[method]()}
</React.Fragment>
)
})}
{controls &&
controls.map((control: string, index: number) => {
control = 'render' + upperFirst(control);
const method:
| 'renderRates'
| 'renderPlay'
| 'renderTime'
| 'renderProcess'
| 'renderVolume'
| 'render' = control as any;
return <React.Fragment key={index}>{this[method]()}</React.Fragment>;
})}
</div>
</div>
);
@ -428,6 +410,6 @@ export class Audio extends React.Component<AudioProps, AudioState> {
@Renderer({
test: /(^|\/)audio/,
name: 'audio',
name: 'audio'
})
export class AudioRenderer extends Audio {}

View File

@ -6,6 +6,6 @@ export default ButtonGroup;
@Renderer({
test: /(^|\/)(?:button|action)\-group$/,
name: 'button-group',
name: 'button-group'
})
export class ButtonGroupRenderer extends ButtonGroup {}

View File

@ -17,7 +17,7 @@ export default class ButtonToolbar extends React.Component<ButtonToolbarProps, o
{Array.isArray(buttons)
? buttons.map((button, key) =>
render(`${key}`, button, {
key,
key
})
)
: null}
@ -28,6 +28,6 @@ export default class ButtonToolbar extends React.Component<ButtonToolbarProps, o
@Renderer({
test: /(^|\/)button-toolbar$/,
name: 'button-toolbar',
name: 'button-toolbar'
})
export class ButtonToolbarRenderer extends ButtonToolbar {}

View File

@ -127,7 +127,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
silentPolling: false,
filterTogglable: false,
filterDefaultVisible: true,
loadDataOnce: false,
loadDataOnce: false
};
control: any;
@ -274,7 +274,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
nextIndex: idx + 1,
hasPrev: idx > 0,
prevIndex: idx - 1,
index: idx,
index: idx
});
} else if (action.actionType === 'ajax') {
const data = ctx;
@ -285,7 +285,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
return store
.saveRemote(action.api, data, {
successMessage: (action.messages && action.messages.success) || (messages && messages.saveSuccess),
errorMessage: (action.messages && action.messages.failed) || (messages && messages.saveFailed),
errorMessage: (action.messages && action.messages.failed) || (messages && messages.saveFailed)
})
.then(async (payload: object) => {
const data = createObject(ctx, payload);
@ -301,7 +301,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
.catch(() => {});
} else if (pickerMode && (action.actionType === 'confirm' || action.actionType === 'submit')) {
return Promise.resolve({
items: store.selectedItems.concat(),
items: store.selectedItems.concat()
});
} else {
onAction(e, action, ctx);
@ -325,7 +325,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
rows: selectedItems,
items: selectedItems,
unSelectedItems: unSelectedItems,
ids,
ids
});
if (action.actionType === 'dialog') {
@ -333,7 +333,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
e,
{
...action,
__from: 'bulkAction',
__from: 'bulkAction'
},
ctx
);
@ -341,8 +341,9 @@ export default class CRUD extends React.Component<CRUDProps, any> {
isEffectiveApi(action.api, ctx) &&
store
.saveRemote(action.api as string, ctx, {
successMessage: (action.messages && action.messages.success) || (messages && messages.saveSuccess),
errorMessage: (action.messages && action.messages.failed) || (messages && messages.saveFailed),
successMessage:
(action.messages && action.messages.success) || (messages && messages.saveSuccess),
errorMessage: (action.messages && action.messages.failed) || (messages && messages.saveFailed)
})
.then(async () => {
if (action.feedback && isVisible(action.feedback, store.data)) {
@ -371,7 +372,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
{
...defaultParams,
...values,
...store.query,
...store.query
},
false,
true,
@ -384,7 +385,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
pickerMode &&
store.updateData({
items: options || [],
items: options || []
});
// 只执行一次。
@ -416,7 +417,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
store.updateQuery(
{
...values,
[pageField || 'page']: jumpToFirstPage ? 1 : store.page,
[pageField || 'page']: jumpToFirstPage ? 1 : store.page
},
syncLocation && env && env.updateLocation
? (location: any) => env.updateLocation(location, replaceLocation)
@ -458,10 +459,10 @@ export default class CRUD extends React.Component<CRUDProps, any> {
return this.handleAction(
undefined,
{
...dialogAction,
...dialogAction
},
createObject(store.data.items[ctx.nextIndex], {
index: ctx.nextIndex,
index: ctx.nextIndex
})
);
} else if (
@ -472,10 +473,10 @@ export default class CRUD extends React.Component<CRUDProps, any> {
return this.handleAction(
undefined,
{
...dialogAction,
...dialogAction
},
createObject(store.data.items[ctx.prevIndex], {
index: ctx.prevIndex,
index: ctx.prevIndex
})
);
} else if (values.length) {
@ -487,7 +488,8 @@ export default class CRUD extends React.Component<CRUDProps, any> {
// 数据保存了,说明列表数据已经无效了,重新刷新。
if (value && (value as any).__saved) {
// 配置了 reload 则跳过自动更新。
dialogAction.reload || this.search(dialogAction.__from ? {[pageField || 'page']: 1} : undefined, undefined, true);
dialogAction.reload ||
this.search(dialogAction.__from ? {[pageField || 'page']: 1} : undefined, undefined, true);
} else if (
value &&
((value.hasOwnProperty('items') && (value as any).items) || value.hasOwnProperty('ids')) &&
@ -518,7 +520,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
store.setCurrentAction({
type: 'button',
actionType: 'dialog',
dialog: dialog,
dialog: dialog
});
store.openDialog(ctx, undefined, confirmed => {
resolve(confirmed);
@ -581,7 +583,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
pageField,
perPageField,
loadDataMode,
syncResponse2Query,
syncResponse2Query
})
.then(value => {
interval &&
@ -607,7 +609,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
const {store, syncLocation, env, pageField, perPageField, autoJumpToTopOnPagerChange} = this.props;
let query: any = {
[pageField || 'page']: page,
[pageField || 'page']: page
};
if (perPage) {
@ -646,7 +648,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
const data: any = createObject(store.data, {
rows,
rowsDiff: diff,
indexes: indexes,
indexes: indexes
});
if (rows.length && rows[0].hasOwnProperty(primaryField || 'id')) {
@ -660,7 +662,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
store
.saveRemote(quickSaveApi, data, {
successMessage: messages && messages.saveFailed,
errorMessage: messages && messages.saveSuccess,
errorMessage: messages && messages.saveSuccess
})
.then(() => {
reload && this.reloadTarget(reload, data);
@ -675,7 +677,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
const data = createObject(store.data, {
item: rows,
modified: diff,
modified: diff
});
const sendData = createObject(data, rows);
@ -754,7 +756,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
const start = (store.page - 1) * store.perPage || 0;
rows = rows.map((item, key) =>
extendObject(item, {
[orderField]: start + key + 1,
[orderField]: start + key + 1
})
);
}
@ -825,7 +827,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
store.updateQuery(
{
...values,
[pageField || 'page']: 1,
[pageField || 'page']: 1
},
syncLocation && env && env.updateLocation ? env.updateLocation : undefined,
pageField,
@ -900,7 +902,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
bulkBtns = bulkActions
.map(item => ({
...item,
...getExprProperties(item as Schema, ctx),
...getExprProperties(item as Schema, ctx)
}))
.filter(item => !item.hidden && item.visible !== false);
}
@ -911,7 +913,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
itemBtns = itemActions
.map(item => ({
...item,
...getExprProperties(item as Schema, itemData),
...getExprProperties(item as Schema, itemData)
}))
.filter(item => !item.hidden && item.visible !== false);
}
@ -947,7 +949,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
bulkBtns = bulkActions
.map(item => ({
...item,
...getExprProperties(item as Schema, ctx),
...getExprProperties(item as Schema, ctx)
}))
.filter(item => !item.hidden && item.visible !== false);
}
@ -958,7 +960,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
itemBtns = itemActions
.map(item => ({
...item,
...getExprProperties(item as Schema, itemData),
...getExprProperties(item as Schema, itemData)
}))
.filter(item => !item.hidden && item.visible !== false);
}
@ -971,17 +973,13 @@ export default class CRUD extends React.Component<CRUDProps, any> {
{
size: 'sm',
...omit(btn, ['visibleOn', 'hiddenOn', 'disabledOn']),
type: 'button',
type: 'button'
},
{
key: `bulk-${index}`,
data: ctx,
disabled: btn.disabled || (btn.requireSelected !== false ? !selectedItems.length : false),
onAction: this.handleBulkAction.bind(
this,
selectedItems.concat(),
unSelectedItems.concat()
),
onAction: this.handleBulkAction.bind(this, selectedItems.concat(), unSelectedItems.concat())
}
)
)}
@ -992,13 +990,13 @@ export default class CRUD extends React.Component<CRUDProps, any> {
{
size: 'sm',
...omit(btn, ['visibleOn', 'hiddenOn', 'disabledOn']),
type: 'button',
type: 'button'
},
{
key: `item-${index}`,
data: itemData,
disabled: btn.disabled,
onAction: this.handleItemAction.bind(this, btn, itemData),
onAction: this.handleItemAction.bind(this, btn, itemData)
}
)
)}
@ -1020,7 +1018,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
{render(
'pagination',
{
type: 'pagination',
type: 'pagination'
},
{
activePage: page,
@ -1057,7 +1055,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
const perPages = (perPageAvailable || [5, 10, 20, 50, 100]).map((item: any) => ({
label: item,
value: item + '',
value: item + ''
}));
return (
@ -1142,7 +1140,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
const children: Array<any> = toolbar
.map((toolbar, index) => ({
dom: this.renderToolbar(toolbar, index, childProps, toolbarRenderer),
toolbar,
toolbar
}))
.filter(item => item.dom);
const len = children.length;
@ -1196,7 +1194,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
createObject(store.filterData, {
items: childProps.items,
selectedItems: childProps.selectedItems,
unSelectedItems: childProps.unSelectedItems,
unSelectedItems: childProps.unSelectedItems
}),
{}
),
@ -1207,7 +1205,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
onAction: this.handleAction,
onChangePage: this.handleChangePage,
onBulkAction: this.handleBulkAction,
$$editable,
$$editable
});
}
@ -1264,9 +1262,11 @@ export default class CRUD extends React.Component<CRUDProps, any> {
×
</span>
<span className={cx('Crud-valueLabel')}>
{labelTpl
? (<Html html={filter(labelTpl, item)} />)
: getVariable(item, labelField || 'label') || getVariable(item, primaryField || 'id')}
{labelTpl ? (
<Html html={filter(labelTpl, item)} />
) : (
getVariable(item, labelField || 'label') || getVariable(item, primaryField || 'id')
)}
</span>
</div>
))}
@ -1303,7 +1303,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
return (
<div
className={cx('Crud', className, {
'is-loading': store.loading,
'is-loading': store.loading
})}
>
{filter && (!store.filterTogggable || store.filterVisible)
@ -1315,14 +1315,14 @@ export default class CRUD extends React.Component<CRUDProps, any> {
submitText: '搜索',
...filter,
type: 'form',
api: null,
api: null
},
{
key: 'filter',
data: store.filterData,
onReset: this.handleFilterReset,
onSubmit: this.handleFilterSubmit,
onInit: this.handleFilterInit,
onInit: this.handleFilterInit
}
)
: null}
@ -1333,7 +1333,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
'body',
{
...rest,
type: mode || 'table',
type: mode || 'table'
},
{
key: 'body',
@ -1360,7 +1360,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
onPopOverClose: this.handleChildPopOverClose,
headerToolbarRender: this.renderHeaderToolbar,
footerToolbarRender: this.renderFooterToolbar,
data: store.mergedData,
data: store.mergedData
}
)}
@ -1369,11 +1369,11 @@ export default class CRUD extends React.Component<CRUDProps, any> {
'info',
{
type: 'spinner',
overlay: true,
overlay: true
},
{
size: 'lg',
key: 'info',
key: 'info'
}
)
: null}
@ -1382,14 +1382,14 @@ export default class CRUD extends React.Component<CRUDProps, any> {
'dialog',
{
...((store.action as Action) && ((store.action as Action).dialog as object)),
type: 'dialog',
type: 'dialog'
},
{
key: 'dialog',
data: store.dialogData,
onConfirm: this.handleDialogConfirm,
onClose: this.handleDialogClose,
show: store.dialogOpen,
show: store.dialogOpen
}
)}
</div>
@ -1400,7 +1400,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
@Renderer({
test: /(^|\/)crud$/,
storeType: CRUDStore.name,
name: 'crud',
name: 'crud'
})
export class CRUDRenderer extends CRUD {
static contextType = ScopedContext;

View File

@ -31,7 +31,7 @@ export class Card extends React.Component<CardProps> {
titleClassName: '',
highlightClassName: '',
subTitleClassName: '',
descClassName: '',
descClassName: ''
};
static propsList: Array<string> = [
@ -43,7 +43,7 @@ export class Card extends React.Component<CardProps> {
'highlightClassName',
'subTitleClassName',
'descClassName',
'hideCheckToggler',
'hideCheckToggler'
];
constructor(props: CardProps) {
@ -63,10 +63,9 @@ export class Card extends React.Component<CardProps> {
let formItem;
if (
!e.currentTarget.contains(target)
|| ~['INPUT', 'TEXTAREA'].indexOf(target.tagName)
|| (formItem = target.closest(`button, a, .${ns}Form-item`))
&& e.currentTarget.contains(formItem)
!e.currentTarget.contains(target) ||
~['INPUT', 'TEXTAREA'].indexOf(target.tagName) ||
((formItem = target.closest(`button, a, .${ns}Form-item`)) && e.currentTarget.contains(formItem))
) {
return;
}
@ -105,7 +104,7 @@ export class Card extends React.Component<CardProps> {
multiple,
hideCheckToggler,
classnames: cx,
classPrefix: ns,
classPrefix: ns
} = this.props;
if (dragging) {
@ -147,7 +146,7 @@ export class Card extends React.Component<CardProps> {
level: 'link',
type: 'button',
...action,
size,
size
},
{
isMenuItem: true,
@ -159,7 +158,7 @@ export class Card extends React.Component<CardProps> {
action.className || `${size ? `Card-action--${size}` : ''}`
),
componentClass: 'a',
onAction: this.handleAction,
onAction: this.handleAction
}
);
})}
@ -182,7 +181,7 @@ export class Card extends React.Component<CardProps> {
if (childNode.type === 'hbox' || childNode.type === 'grid') {
return render(region, node, {
key,
itemRender: this.itemRender,
itemRender: this.itemRender
}) as JSX.Element;
}
@ -197,7 +196,7 @@ export class Card extends React.Component<CardProps> {
const {render, classnames: cx, itemIndex} = props;
const data = this.props.data;
if (!isVisible(field, data)) {
return ;
return;
}
const $$id = field.$$id ? `${field.$$id}-field` : '';
@ -215,7 +214,7 @@ export class Card extends React.Component<CardProps> {
...field,
field: field,
$$id,
type: 'card-item-field',
type: 'card-item-field'
},
{
className: cx('Card-fieldValue', field.className),
@ -224,7 +223,7 @@ export class Card extends React.Component<CardProps> {
value: field.name ? resolveVariable(field.name, data) : undefined,
popOverContainer: this.getPopOverContainer,
onAction: this.handleAction,
onQuickChange: this.handleQuickChange,
onQuickChange: this.handleQuickChange
}
) as JSX.Element
}
@ -260,7 +259,7 @@ export class Card extends React.Component<CardProps> {
checkOnItemClick,
checkable,
classnames: cx,
classPrefix: ns,
classPrefix: ns
} = this.props;
let heading = null;
@ -275,7 +274,7 @@ export class Card extends React.Component<CardProps> {
subTitle: subTitleTpl,
subTitlePlaceholder,
desc: descTpl,
descPlaceholder,
descPlaceholder
} = header;
const highlight = !!evalExpression(highlightTpl, data as object);
@ -305,7 +304,7 @@ export class Card extends React.Component<CardProps> {
{subTitle || subTitlePlaceholder ? (
<div className={cx('Card-subTitle', header.subTitleClassName || subTitleClassName)}>
{render('sub-title', subTitle || subTitlePlaceholder, {
className: cx(!subTitle ? 'Card-placeholder' : undefined),
className: cx(!subTitle ? 'Card-placeholder' : undefined)
})}
</div>
) : null}
@ -313,7 +312,7 @@ export class Card extends React.Component<CardProps> {
{desc || descPlaceholder ? (
<div className={cx('Card-desc', header.descClassName || descClassName)}>
{render('desc', desc || descPlaceholder, {
className: !desc ? 'text-muted' : undefined,
className: !desc ? 'text-muted' : undefined
})}
</div>
) : null}
@ -340,13 +339,13 @@ export class Card extends React.Component<CardProps> {
@Renderer({
test: /(^|\/)card$/,
name: 'card',
name: 'card'
})
export class CardRenderer extends Card {}
@Renderer({
test: /(^|\/)card-item-field$/,
name: 'card-item',
name: 'card-item'
})
@QuickEdit()
@PopOver()
@ -354,7 +353,7 @@ export class CardRenderer extends Card {}
export class CardItemFieldRenderer extends TableCell {
static defaultProps = {
...TableCell.defaultProps,
wrapperComponent: 'div',
wrapperComponent: 'div'
};
static propsList = ['quickEdit', 'popOver', 'copyable', ...TableCell.propsList];
@ -381,7 +380,7 @@ export class CardItemFieldRenderer extends TableCell {
const schema = {
...field,
className: innerClassName,
type: (field && field.type) || 'plain',
type: (field && field.type) || 'plain'
};
let body = children
@ -389,7 +388,7 @@ export class CardItemFieldRenderer extends TableCell {
: render('field', schema, {
...rest,
value,
data,
data
});
if (width) {

View File

@ -69,7 +69,7 @@ export default class Cards extends React.Component<GridProps, object> {
'itemDraggableOn',
'masonryLayout',
'items',
'valueField',
'valueField'
];
static defaultProps: Partial<GridProps> = {
className: '',
@ -83,7 +83,7 @@ export default class Cards extends React.Component<GridProps, object> {
hideCheckToggler: false,
masonryLayout: false,
affixHeader: true,
itemsClassName: '',
itemsClassName: ''
};
dragTip?: HTMLElement;
@ -92,7 +92,7 @@ export default class Cards extends React.Component<GridProps, object> {
body?: any;
// fixAlignmentLazy: Function;
unSensor: Function;
renderedToolbars:Array<string>;
renderedToolbars: Array<string>;
constructor(props: GridProps) {
super(props);
@ -148,7 +148,7 @@ export default class Cards extends React.Component<GridProps, object> {
multiple,
hideCheckToggler,
itemCheckableOn,
itemDraggableOn,
itemDraggableOn
} = this.props;
store.update({
@ -159,7 +159,7 @@ export default class Cards extends React.Component<GridProps, object> {
multiple,
hideCheckToggler,
itemCheckableOn,
itemDraggableOn,
itemDraggableOn
});
Cards.syncItems(store, this.props);
@ -192,7 +192,7 @@ export default class Cards extends React.Component<GridProps, object> {
'multiple',
'hideCheckToggler',
'itemCheckableOn',
'itemDraggableOn',
'itemDraggableOn'
],
props,
nextProps
@ -206,7 +206,7 @@ export default class Cards extends React.Component<GridProps, object> {
multiple: nextProps.multiple,
hideCheckToggler: nextProps.hideCheckToggler,
itemCheckableOn: nextProps.itemCheckableOn,
itemDraggableOn: nextProps.itemDraggableOn,
itemDraggableOn: nextProps.itemDraggableOn
});
}
@ -311,7 +311,7 @@ export default class Cards extends React.Component<GridProps, object> {
null,
{
actionType: 'ajax',
api: saveImmediately.api,
api: saveImmediately.api
},
values
);
@ -403,7 +403,7 @@ export default class Cards extends React.Component<GridProps, object> {
}
store.exchange(e.oldIndex, e.newIndex);
},
}
});
}
@ -420,16 +420,20 @@ export default class Cards extends React.Component<GridProps, object> {
actions.unshift({
type: 'button',
children: btn
})
});
}
if (region === 'header' && !~this.renderedToolbars.indexOf('drag-toggler') && (btn = this.renderDragToggler())) {
if (
region === 'header' &&
!~this.renderedToolbars.indexOf('drag-toggler') &&
(btn = this.renderDragToggler())
) {
actions.unshift({
type: 'button',
children: btn
})
});
}
return Array.isArray(actions) && actions.length ? (
<div className={cx('Cards-actions')}>
{actions.map((action, key) =>
@ -437,12 +441,12 @@ export default class Cards extends React.Component<GridProps, object> {
`action/${key}`,
{
type: 'button',
...action,
...action
},
{
onAction: this.handleAction,
key,
btnDisabled: store.dragging,
btnDisabled: store.dragging
}
)
)}
@ -517,7 +521,7 @@ export default class Cards extends React.Component<GridProps, object> {
showHeader,
render,
store,
classnames: cx,
classnames: cx
} = this.props;
if (showHeader === false) {
@ -525,12 +529,15 @@ export default class Cards extends React.Component<GridProps, object> {
}
const child = headerToolbarRender
? headerToolbarRender({
...this.props,
selectedItems: store.selectedItems.map(item => item.data),
items: store.items.map(item => item.data),
unSelectedItems: store.unSelectedItems.map(item => item.data),
}, this.renderToolbar)
? headerToolbarRender(
{
...this.props,
selectedItems: store.selectedItems.map(item => item.data),
items: store.items.map(item => item.data),
unSelectedItems: store.unSelectedItems.map(item => item.data)
},
this.renderToolbar
)
: null;
const actions = this.renderActions('header');
const toolbarNode =
@ -562,7 +569,7 @@ export default class Cards extends React.Component<GridProps, object> {
render,
showFooter,
store,
classnames: cx,
classnames: cx
} = this.props;
if (showFooter === false) {
@ -570,15 +577,18 @@ export default class Cards extends React.Component<GridProps, object> {
}
const child = footerToolbarRender
? footerToolbarRender({
...this.props,
selectedItems: store.selectedItems.map(item => item.data),
items: store.items.map(item => item.data),
unSelectedItems: store.unSelectedItems.map(item => item.data),
}, this.renderToolbar)
? footerToolbarRender(
{
...this.props,
selectedItems: store.selectedItems.map(item => item.data),
items: store.items.map(item => item.data),
unSelectedItems: store.unSelectedItems.map(item => item.data)
},
this.renderToolbar
)
: null;
const actions = this.renderActions('footer');
const toolbarNode =
actions || child ? (
<div className={cx('Cards-toolbar')} key="footer-toolbar">
@ -595,12 +605,8 @@ export default class Cards extends React.Component<GridProps, object> {
}
renderCheckAll() {
const {
store,
multiple,
selectable,
} = this.props;
const {store, multiple, selectable} = this.props;
if (!store.selectable || !multiple || !selectable || store.dragging || !store.items.length) {
return null;
}
@ -619,13 +625,8 @@ export default class Cards extends React.Component<GridProps, object> {
}
renderDragToggler() {
const {
store,
multiple,
selectable,
env
} = this.props;
const {store, multiple, selectable, env} = this.props;
if (!store.draggable || store.items.length < 2) {
return null;
}
@ -679,7 +680,7 @@ export default class Cards extends React.Component<GridProps, object> {
checkOnItemClick,
masonryLayout,
itemsClassName,
classnames: cx,
classnames: cx
} = this.props;
this.renderedToolbars = []; // 用来记录哪些 toolbar 已经渲染了,已经渲染了就不重复渲染了。
@ -710,7 +711,7 @@ export default class Cards extends React.Component<GridProps, object> {
<div
ref={this.bodyRef}
className={cx('Cards', className, {
'Cards--unsaved': !!store.modified || !!store.moved,
'Cards--unsaved': !!store.modified || !!store.moved
})}
>
{affixHeader ? (
@ -730,13 +731,13 @@ export default class Cards extends React.Component<GridProps, object> {
`${index}`,
{
type: 'card',
...card,
...card
},
{
className: cx((card && card.className) || '', {
'is-checked': item.checked,
'is-modified': item.modified,
'is-moved': item.moved,
'is-moved': item.moved
}),
item,
intemIndex: item.index,
@ -752,7 +753,7 @@ export default class Cards extends React.Component<GridProps, object> {
checkOnItemClick,
onAction,
onCheck: this.handleCheck,
onQuickChange: store.dragging ? null : this.handleQuickChange,
onQuickChange: store.dragging ? null : this.handleQuickChange
}
)}
</div>
@ -773,7 +774,7 @@ export default class Cards extends React.Component<GridProps, object> {
test: /(^|\/)(?:crud\/body\/grid|cards)$/,
name: 'cards',
storeType: ListStore.name,
weight: -100, // 默认的 grid 不是这样,这个只识别 crud 下面的 grid
weight: -100 // 默认的 grid 不是这样,这个只识别 crud 下面的 grid
})
export class CardsRenderer extends Cards {
dragging: boolean;

View File

@ -88,9 +88,10 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
nextAnimation: ''
};
componentWillReceiveProps(nextProps:CarouselProps) {
componentWillReceiveProps(nextProps: CarouselProps) {
const currentOptions = this.state.options;
const nextOptions = nextProps.value || nextProps.options || resolveVariable(nextProps.name, nextProps.data) || [];
const nextOptions =
nextProps.value || nextProps.options || resolveVariable(nextProps.name, nextProps.data) || [];
if (isArrayChilrenModified(currentOptions, nextOptions)) {
this.setState({
options: nextOptions
@ -103,11 +104,11 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
}
componentWillUnmount() {
this.clearAutoTimeout()
this.clearAutoTimeout();
}
@autobind
prepareAutoSlide () {
prepareAutoSlide() {
if (this.state.options.length < 2) {
return;
}
@ -119,14 +120,14 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
}
@autobind
autoSlide (rel?:string) {
autoSlide(rel?: string) {
this.clearAutoTimeout();
const {animation} = this.props;
let {nextAnimation} = this.state;
switch (rel) {
case 'prev':
animation === 'slide' ? nextAnimation = 'slideRight' : nextAnimation = '';
animation === 'slide' ? (nextAnimation = 'slideRight') : (nextAnimation = '');
this.transitFramesTowards('right', nextAnimation);
break;
case 'next':
@ -140,7 +141,7 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
}
@autobind
transitFramesTowards (direction:string, nextAnimation: string) {
transitFramesTowards(direction: string, nextAnimation: string) {
let {current} = this.state;
switch (direction) {
@ -159,7 +160,7 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
}
@autobind
getFrameId (pos?:string) {
getFrameId(pos?: string) {
const {options, current} = this.state;
const total = options.length;
switch (pos) {
@ -173,17 +174,17 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
}
@autobind
next () {
next() {
this.autoSlide('next');
}
@autobind
prev () {
prev() {
this.autoSlide('prev');
}
@autobind
clearAutoTimeout () {
clearAutoTimeout() {
clearTimeout(this.intervalTimeout);
clearTimeout(this.durationTimeout);
}
@ -197,11 +198,11 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
>
{Array.from({length: options.length}).map((_, i) =>
<span key={i} className={cx('Carousel-dot', current === i ? 'is-active' : '')}></span>
)}
{Array.from({length: options.length}).map((_, i) => (
<span key={i} className={cx('Carousel-dot', current === i ? 'is-active' : '')} />
))}
</div>
)
);
}
renderArrows() {
@ -212,10 +213,14 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
>
<div className={cx('Carousel-leftArrow')} onClick={this.prev}><Icon icon="left-arrow" className="icon" /></div>
<div className={cx('Carousel-rightArrow')} onClick={this.next}><Icon icon="right-arrow" className="icon" /></div>
<div className={cx('Carousel-leftArrow')} onClick={this.prev}>
<Icon icon="left-arrow" className="icon" />
</div>
<div className={cx('Carousel-rightArrow')} onClick={this.next}>
<Icon icon="right-arrow" className="icon" />
</div>
</div>
)
);
}
@autobind
@ -249,19 +254,14 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
data,
name
} = this.props;
const {
options,
showArrows,
current,
nextAnimation
} = this.state;
const {options, showArrows, current, nextAnimation} = this.state;
let body:JSX.Element | null = null;
let body: JSX.Element | null = null;
let carouselStyles: {
[propName: string]: string;
} = {};
width ? carouselStyles.width = width + 'px' : '';
height ? carouselStyles.height = height + 'px' : '';
width ? (carouselStyles.width = width + 'px') : '';
height ? (carouselStyles.height = height + 'px') : '';
const [dots, arrows] = [controls.indexOf('dots') > -1, controls.indexOf('arrows') > -1];
const animationName = nextAnimation || animation;
@ -272,24 +272,24 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
className={cx('Carousel-container')}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
>
{options.map((option:any, key:number) => (
<Transition
mountOnEnter
unmountOnExit
in={key === current}
timeout={500}
key={key}
>
{(status:string) => {
>
{options.map((option: any, key: number) => (
<Transition mountOnEnter unmountOnExit in={key === current} timeout={500} key={key}>
{(status: string) => {
if (status === ENTERING) {
this.wrapperRef.current && this.wrapperRef.current.childNodes.forEach((item:HTMLElement) => item.offsetHeight);
this.wrapperRef.current &&
this.wrapperRef.current.childNodes.forEach(
(item: HTMLElement) => item.offsetHeight
);
}
return (
<div className={cx('Carousel-item', animationName, animationStyles[status])}>
{render(`${current}/body`, itemSchema ? itemSchema : defaultSchema, {
data: createObject(data, isObject(option) ? option : {item: option, [name]: option})
data: createObject(
data,
isObject(option) ? option : {item: option, [name]: option}
)
})}
</div>
);
@ -312,6 +312,6 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
@Renderer({
test: /(^|\/)carousel/,
name: 'carousel',
name: 'carousel'
})
export class CarouselRenderer extends Carousel {}

View File

@ -25,7 +25,7 @@ export interface ChartProps extends RendererProps {
export class Chart extends React.Component<ChartProps> {
static defaultProps: Partial<ChartProps> = {
offsetY: 50,
replaceChartOption: false,
replaceChartOption: false
};
static propsList: Array<string> = [];
@ -104,7 +104,7 @@ export class Chart extends React.Component<ChartProps> {
const height = ref.offsetHeight;
this.echarts.resize({
width,
height,
height
});
});
@ -136,10 +136,9 @@ export class Chart extends React.Component<ChartProps> {
}
this.echarts && this.echarts.showLoading();
env
.fetcher(api, store.data, {
cancelExecutor: (executor: Function) => (this.reloadCancel = executor),
})
env.fetcher(api, store.data, {
cancelExecutor: (executor: Function) => (this.reloadCancel = executor)
})
.then(result => {
delete this.reloadCancel;
this.renderChart(result.data || {});
@ -212,7 +211,7 @@ export class Chart extends React.Component<ChartProps> {
@Renderer({
test: /(^|\/)chart$/,
storeType: ServiceStore.name,
name: 'chart',
name: 'chart'
})
export class ChartRenderer extends Chart {
static contextType = ScopedContext;

View File

@ -1,6 +1,6 @@
import React from 'react';
import { Renderer, RendererProps } from '../factory';
import { Collapse as BasicCollapse } from '../components/Collapse';
import {Renderer, RendererProps} from '../factory';
import {Collapse as BasicCollapse} from '../components/Collapse';
export interface CollapseProps extends RendererProps {
title?: string; // 标题
@ -24,7 +24,7 @@ export default class Collapse extends React.Component<CollapseProps, CollapseSta
'headingComponent',
'bodyClassName',
'collapsed',
'headingClassName',
'headingClassName'
];
static defaultProps: Partial<CollapseProps> = {
@ -33,11 +33,11 @@ export default class Collapse extends React.Component<CollapseProps, CollapseSta
className: '',
headingClassName: '',
bodyClassName: '',
collapsable: true,
collapsable: true
};
state = {
collapsed: false,
collapsed: false
};
constructor(props: CollapseProps) {
@ -52,15 +52,16 @@ export default class Collapse extends React.Component<CollapseProps, CollapseSta
if (props.collapsed !== nextProps.collapsed) {
this.setState({
collapsed: !!nextProps.collapsed,
collapsed: !!nextProps.collapsed
});
}
}
toggleCollapsed() {
this.props.collapsable !== false && this.setState({
collapsed: !this.state.collapsed,
});
this.props.collapsable !== false &&
this.setState({
collapsed: !this.state.collapsed
});
}
render() {
@ -77,7 +78,7 @@ export default class Collapse extends React.Component<CollapseProps, CollapseSta
body,
bodyClassName,
render,
collapsable,
collapsable
} = this.props;
return (
@ -87,13 +88,16 @@ export default class Collapse extends React.Component<CollapseProps, CollapseSta
{
'is-collapsed': this.state.collapsed,
[`Collapse--${size}`]: size,
'Collapse--collapsable': collapsable,
'Collapse--collapsable': collapsable
},
className
)}
>
{title ? (
<HeadingComponent onClick={this.toggleCollapsed} className={cx(`Collapse-header`, headingClassName)}>
<HeadingComponent
onClick={this.toggleCollapsed}
className={cx(`Collapse-header`, headingClassName)}
>
{render('heading', title)}
{collapsable && <span className={cx('Collapse-arrow')} />}
</HeadingComponent>
@ -117,6 +121,6 @@ export default class Collapse extends React.Component<CollapseProps, CollapseSta
@Renderer({
test: /(^|\/)collapse$/,
name: 'collapse',
name: 'collapse'
})
export class CollapseRenderer extends Collapse {}

View File

@ -11,7 +11,7 @@ export interface ContainerProps extends RendererProps {
export default class Container<T> extends React.Component<ContainerProps & T, object> {
static propsList: Array<string> = ['body', 'className'];
static defaultProps = {
className: '',
className: ''
};
renderBody(): JSX.Element | null {
@ -39,6 +39,6 @@ export default class Container<T> extends React.Component<ContainerProps & T, ob
@Renderer({
test: /(^|\/)container$/,
name: 'container',
name: 'container'
})
export class ContainerRenderer extends Container<{}> {}

View File

@ -29,10 +29,10 @@ export class DateField extends React.Component<DateProps, DateState> {
// 动态显示相对时间时,用来触发视图更新
state: DateState = {
random: 0
}
};
componentDidMount() {
const { fromNow, updateFrequency } = this.props;
const {fromNow, updateFrequency} = this.props;
if (fromNow && updateFrequency) {
this.refreshInterval = setInterval(() => {
@ -48,7 +48,7 @@ export class DateField extends React.Component<DateProps, DateState> {
}
render() {
const { value, valueFormat, format, placeholder, fromNow, className, classnames: cx } = this.props;
const {value, valueFormat, format, placeholder, fromNow, className, classnames: cx} = this.props;
let viewValue: React.ReactNode = <span className="text-muted">{placeholder}</span>;
if (value) {
@ -57,11 +57,9 @@ export class DateField extends React.Component<DateProps, DateState> {
viewValue = ISODate.isValid()
? ISODate.format(format)
: (
NormalDate.isValid()
? NormalDate.format(format)
: false
);
: NormalDate.isValid()
? NormalDate.format(format)
: false;
}
if (fromNow) {
@ -76,33 +74,33 @@ export class DateField extends React.Component<DateProps, DateState> {
@Renderer({
test: /(^|\/)date$/,
name: 'date-field',
name: 'date-field'
})
export class DateFieldRenderer extends DateField {
static defaultProps: Partial<DateProps> = {
...DateField.defaultProps,
format: 'YYYY-MM-DD',
format: 'YYYY-MM-DD'
};
}
@Renderer({
test: /(^|\/)datetime$/,
name: 'datetime-field',
name: 'datetime-field'
})
export class DateTimeFieldRenderer extends DateField {
static defaultProps: Partial<DateProps> = {
...DateField.defaultProps,
format: 'YYYY-MM-DD HH:mm:ss',
format: 'YYYY-MM-DD HH:mm:ss'
};
}
@Renderer({
test: /(^|\/)time$/,
name: 'time-field',
name: 'time-field'
})
export class TimeFieldRenderer extends DateField {
static defaultProps: Partial<DateProps> = {
...DateField.defaultProps,
format: 'HH:mm',
format: 'HH:mm'
};
}

View File

@ -1,18 +1,18 @@
import React from 'react';
import PropTypes from 'prop-types';
import Scoped, { ScopedContext, IScopedContext } from '../Scoped';
import { Renderer, RendererProps } from '../factory';
import { ServiceStore, IServiceStore } from '../store/service';
import { observer } from 'mobx-react';
import { SchemaNode, Schema, Action } from '../types';
import { filter } from '../utils/tpl';
import Scoped, {ScopedContext, IScopedContext} from '../Scoped';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {observer} from 'mobx-react';
import {SchemaNode, Schema, Action} from '../types';
import {filter} from '../utils/tpl';
import Modal from '../components/Modal';
import findLast = require('lodash/findLast');
import { guid, chainFunctions, isVisible } from '../utils/helper';
import { reaction } from 'mobx';
import { Icon } from '../components/icons';
import { ModalStore, IModalStore } from '../store/modal';
import { findDOMNode } from 'react-dom';
import {guid, chainFunctions, isVisible} from '../utils/helper';
import {reaction} from 'mobx';
import {Icon} from '../components/icons';
import {ModalStore, IModalStore} from '../store/modal';
import {findDOMNode} from 'react-dom';
export interface DialogProps extends RendererProps {
title?: string; // 标题
@ -53,7 +53,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
'show',
'body',
'showCloseButton',
'actions',
'actions'
];
static defaultProps: Partial<DialogProps> = {
title: '弹框',
@ -63,7 +63,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
lazyRender: false,
showCloseButton: true,
wrapperComponent: Modal,
closeOnEsc: false,
closeOnEsc: false
};
reaction: any;
@ -72,7 +72,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
super(props);
this.state = {
entered: !!this.props.show,
entered: !!this.props.show
};
this.handleSelfClose = this.handleSelfClose.bind(this);
this.handleAction = this.handleAction.bind(this);
@ -110,7 +110,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
}
buildActions(): Array<Action> {
const { actions, confirm } = this.props;
const {actions, confirm} = this.props;
if (typeof actions !== 'undefined') {
return actions;
@ -120,7 +120,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
ret.push({
type: 'button',
actionType: 'cancel',
label: '取消',
label: '取消'
});
if (confirm) {
@ -128,7 +128,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
type: 'button',
actionType: 'confirm',
label: '确认',
primary: true,
primary: true
});
}
@ -136,7 +136,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
}
handleSelfClose() {
const { onClose, store } = this.props;
const {onClose, store} = this.props;
// clear error
store.updateMessage();
@ -144,7 +144,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
}
handleAction(e: React.UIEvent<any>, action: Action, data: object) {
const { store, onAction } = this.props;
const {store, onAction} = this.props;
if (action.type === 'reset') {
store.reset();
@ -156,7 +156,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
}
handleDialogConfirm(values: object[], action: Action, ...args: Array<any>) {
const { store } = this.props;
const {store} = this.props;
if (action.mergeData && values.length === 1 && values[0]) {
store.updateData(values[0]);
@ -172,7 +172,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
}
handleDialogClose(...args: Array<any>) {
const { store } = this.props;
const {store} = this.props;
const action = store.action as Action;
const dialog = action.dialog as any;
@ -185,7 +185,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
}
handleDrawerConfirm(values: object[], action: Action, ...args: Array<any>) {
const { store } = this.props;
const {store} = this.props;
if (action.mergeData && values.length === 1 && values[0]) {
store.updateData(values[0]);
@ -201,7 +201,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
}
handleDrawerClose(...args: Array<any>) {
const { store } = this.props;
const {store} = this.props;
const action = store.action as Action;
const drawer = action.drawer as any;
@ -216,7 +216,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
handleEntered() {
this.state.entered ||
this.setState({
entered: true,
entered: true
});
const activeElem = document.activeElement as HTMLElement;
@ -227,32 +227,32 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
}
handleExited() {
const { store } = this.props;
const {store} = this.props;
store.reset();
this.state.entered &&
this.setState({
entered: false,
entered: false
});
}
handleFormInit(data: any) {
const { store } = this.props;
const {store} = this.props;
store.setFormData(data);
}
handleFormChange(data: any) {
const { store } = this.props;
const {store} = this.props;
store.setFormData(data);
}
handleFormSaved(data: any, response: any) {
const { store } = this.props;
const {store} = this.props;
store.setFormData({
...data,
...response,
...response
});
}
@ -266,7 +266,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
store.setCurrentAction({
type: 'button',
actionType: 'dialog',
dialog: dialog,
dialog: dialog
});
store.openDialog(ctx, undefined, confirmed => {
resolve(confirmed);
@ -275,7 +275,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
}
renderBody(body: SchemaNode, key?: any): React.ReactNode {
let { render, store } = this.props;
let {render, store} = this.props;
if (Array.isArray(body)) {
return body.map((body, key) => this.renderBody(body, key));
@ -283,9 +283,9 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
let subProps: any = {
key,
disabled: body && (body as any).disabled || store.loading,
disabled: (body && (body as any).disabled) || store.loading,
onAction: this.handleAction,
onFinished: this.handleChildFinished,
onFinished: this.handleChildFinished
};
if (!(body as Schema).type) {
@ -299,7 +299,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
mode: 'horizontal',
wrapWithPanel: false,
submitText: null,
...schema,
...schema
};
// 同步数据到 Dialog 层,方便 actions 根据表单数据联动。
@ -318,7 +318,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
return null;
}
const { store, render, classnames: cx } = this.props;
const {store, render, classnames: cx} = this.props;
return (
<div className={cx('Modal-footer')}>
@ -326,15 +326,15 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
<div className={cx('Dialog-info')} key="info">
{store.loading
? render(
'info',
{
type: 'spinner',
},
{
key: 'info',
size: 'sm',
}
)
'info',
{
type: 'spinner'
},
{
key: 'info',
size: 'sm'
}
)
: null}
{store.error ? <span className={cx('Dialog-error')}>{store.msg}</span> : null}
</div>
@ -344,7 +344,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
data: store.formData,
onAction: this.handleAction,
key,
disabled: action.disabled || store.loading,
disabled: action.disabled || store.loading
})
)}
</div>
@ -369,7 +369,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
showCloseButton,
env,
classnames: cx,
classPrefix,
classPrefix
} = this.props;
// console.log('Render Dialog');
@ -408,7 +408,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
</a>
) : null}
{render('title', title, {
data: store.formData,
data: store.formData
})}
</div>
) : showCloseButton !== false && !store.loading ? (
@ -419,8 +419,8 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
{header
? render('header', header, {
data: store.formData,
})
data: store.formData
})
: null}
{!this.state.entered && lazyRender ? (
@ -433,40 +433,40 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
{body
? render(
'drawer',
{
// 支持嵌套
...((store.action as Action) && ((store.action as Action).drawer as object)),
type: 'drawer',
},
{
key: 'drawer',
data: store.drawerData,
onConfirm: this.handleDrawerConfirm,
onClose: this.handleDrawerClose,
show: store.drawerOpen,
onAction: this.handleAction,
}
)
'drawer',
{
// 支持嵌套
...((store.action as Action) && ((store.action as Action).drawer as object)),
type: 'drawer'
},
{
key: 'drawer',
data: store.drawerData,
onConfirm: this.handleDrawerConfirm,
onClose: this.handleDrawerClose,
show: store.drawerOpen,
onAction: this.handleAction
}
)
: null}
{body
? render(
'dialog',
{
// 支持嵌套
...((store.action as Action) && ((store.action as Action).dialog as object)),
type: 'dialog',
},
{
key: 'dialog',
data: store.dialogData,
onConfirm: this.handleDialogConfirm,
onClose: this.handleDialogClose,
show: store.dialogOpen,
onAction: this.handleAction,
}
)
'dialog',
{
// 支持嵌套
...((store.action as Action) && ((store.action as Action).dialog as object)),
type: 'dialog'
},
{
key: 'dialog',
data: store.dialogData,
onConfirm: this.handleDialogConfirm,
onClose: this.handleDialogClose,
show: store.dialogOpen,
onAction: this.handleAction
}
)
: null}
</Wrapper>
);
@ -478,7 +478,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
storeType: ModalStore.name,
storeExtendsData: false,
name: 'dialog',
isolateScope: true,
isolateScope: true
})
export class DialogRenderer extends Dialog {
static contextType = ScopedContext;
@ -504,7 +504,7 @@ export class DialogRenderer extends Dialog {
const components = scoped.getComponents();
const targets: Array<any> = [];
const { onConfirm, store } = this.props;
const {onConfirm, store} = this.props;
if (action.target) {
targets.push(
@ -532,7 +532,7 @@ export class DialogRenderer extends Dialog {
target.doAction(
{
...action,
from: this.$$id,
from: this.$$id
},
ctx,
true
@ -568,9 +568,9 @@ export class DialogRenderer extends Dialog {
action: Action,
data: object,
throwErrors: boolean = false,
delegate?: boolean,
delegate?: boolean
) {
const { onAction, store, onConfirm, env } = this.props;
const {onAction, store, onConfirm, env} = this.props;
if (action.from === this.$$id) {
return onAction ? onAction(e, action, data, throwErrors, true) : false;
@ -587,7 +587,7 @@ export class DialogRenderer extends Dialog {
this.tryChildrenToHandle(
{
...action,
actionType: 'submit',
actionType: 'submit'
},
data,
action
@ -597,7 +597,7 @@ export class DialogRenderer extends Dialog {
this.tryChildrenToHandle(
{
...action,
actionType: 'submit',
actionType: 'submit'
},
data,
action
@ -616,8 +616,8 @@ export class DialogRenderer extends Dialog {
} else if (action.actionType === 'ajax') {
store
.saveRemote(action.api as string, data, {
successMessage: (action.messages && action.messages.success) ,
errorMessage: (action.messages && action.messages.failed),
successMessage: action.messages && action.messages.success,
errorMessage: action.messages && action.messages.failed
})
.then(async () => {
if (action.feedback && isVisible(action.feedback, store.data)) {

View File

@ -5,27 +5,23 @@ import {filter} from '../utils/tpl';
import cx from 'classnames';
export interface DividerProps extends RendererProps {
lineStyle: 'dashed' | 'solid'
lineStyle: 'dashed' | 'solid';
}
export default class Divider extends React.Component<DividerProps, object> {
static defaultProps:Pick<DividerProps, 'className' | 'lineStyle'> = {
static defaultProps: Pick<DividerProps, 'className' | 'lineStyle'> = {
className: '',
lineStyle: 'dashed'
};
render() {
const {
classnames: cx,
className,
lineStyle
} = this.props;
const {classnames: cx, className, lineStyle} = this.props;
return <div className={cx('Divider', lineStyle ? `Divider--${lineStyle}` : '', className)} />;
}
}
@Renderer({
test: /(^|\/)(?:divider|hr)$/,
name: 'divider',
name: 'divider'
})
export class DividerRenderer extends Divider {}

View File

@ -12,7 +12,7 @@ import {guid, chainFunctions, isVisible} from '../utils/helper';
import {reaction} from 'mobx';
import {findDOMNode} from 'react-dom';
import {IModalStore, ModalStore} from '../store/modal';
import { filter } from '../utils/tpl';
import {filter} from '../utils/tpl';
export interface DrawerProps extends RendererProps {
title?: string; // 标题
@ -58,14 +58,14 @@ export default class Drawer extends React.Component<DrawerProps, object> {
position: 'right',
resizable: false,
overlay: true,
closeOnEsc: false,
closeOnEsc: false
};
reaction: any;
$$id: string = guid();
drawer: any;
state = {
resizeCoord: 0,
resizeCoord: 0
};
constructor(props: DrawerProps) {
super(props);
@ -116,7 +116,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
ret.push({
type: 'button',
actionType: 'close',
label: '取消',
label: '取消'
});
if (confirm) {
@ -124,7 +124,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
type: 'button',
actionType: 'confirm',
label: '确认',
primary: true,
primary: true
});
}
@ -230,7 +230,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
store.setFormData({
...data,
...response,
...response
});
}
@ -251,7 +251,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
key,
disabled: store.loading,
onAction: this.handleAction,
onFinished: this.handleChildFinished,
onFinished: this.handleChildFinished
};
if (schema.type === 'form') {
@ -259,7 +259,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
mode: 'horizontal',
wrapWithPanel: false,
submitText: null,
...schema,
...schema
};
// 同步数据到 Dialog 层,方便 actions 根据表单数据联动。
@ -288,10 +288,10 @@ export default class Drawer extends React.Component<DrawerProps, object> {
? render(
'info',
{
type: 'spinner',
type: 'spinner'
},
{
key: 'info',
key: 'info'
}
)
: null}
@ -303,7 +303,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
onAction: this.handleAction,
data: store.formData,
key,
disabled: action.disabled || store.loading,
disabled: action.disabled || store.loading
})
)}
</div>
@ -348,7 +348,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
e.clientY -
resizeCtrl.offsetHeight -
parseInt(drawerHeight.substring(0, drawerHeight.length - 2))) ||
0,
0
});
document.body.addEventListener('mousemove', this.bindResize);
@ -388,7 +388,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
store.setCurrentAction({
type: 'button',
actionType: 'dialog',
dialog: dialog,
dialog: dialog
});
store.openDialog(ctx, undefined, confirmed => {
resolve(confirmed);
@ -415,7 +415,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
overlay,
closeOnOutside,
classPrefix: ns,
classnames: cx,
classnames: cx
} = this.props;
const Container = wrapperComponent || DrawerContainer;
@ -439,13 +439,13 @@ export default class Drawer extends React.Component<DrawerProps, object> {
{title ? (
<div className={cx('Drawer-title')}>
{render('title', title, {
data: store.formData,
data: store.formData
})}
</div>
) : null}
{header
? render('header', header, {
data: store.formData,
data: store.formData
})
: null}
</div>
@ -459,7 +459,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
'dialog',
{
...((store.action as Action) && ((store.action as Action).dialog as object)),
type: 'dialog',
type: 'dialog'
},
{
key: 'dialog',
@ -467,7 +467,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
onConfirm: this.handleDialogConfirm,
onClose: this.handleDialogClose,
onAction: this.handleAction,
show: store.dialogOpen,
show: store.dialogOpen
}
)
: null}
@ -477,7 +477,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
'drawer',
{
...((store.action as Action) && ((store.action as Action).drawer as object)),
type: 'drawer',
type: 'drawer'
},
{
key: 'drawer',
@ -485,7 +485,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
onConfirm: this.handleDrawerConfirm,
onClose: this.handleDrawerClose,
onAction: this.handleAction,
show: store.drawerOpen,
show: store.drawerOpen
}
)
: null}
@ -501,7 +501,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
storeType: ModalStore.name,
storeExtendsData: false,
name: 'drawer',
isolateScope: true,
isolateScope: true
})
export class DrawerRenderer extends Drawer {
static contextType = ScopedContext;
@ -555,7 +555,7 @@ export class DrawerRenderer extends Drawer {
target.doAction(
{
...action,
from: this.$$id,
from: this.$$id
},
ctx,
true
@ -617,8 +617,8 @@ export class DrawerRenderer extends Drawer {
} else if (action.actionType === 'ajax') {
store
.saveRemote(action.api as string, data, {
successMessage: (action.messages && action.messages.success) ,
errorMessage: (action.messages && action.messages.failed),
successMessage: action.messages && action.messages.success,
errorMessage: action.messages && action.messages.failed
})
.then(async () => {
if (action.feedback && isVisible(action.feedback, store.data)) {

View File

@ -24,11 +24,11 @@ export interface DropDownButtonState {
export default class DropDownButton extends React.Component<DropDownButtonProps, DropDownButtonState> {
state: DropDownButtonState = {
isOpened: this.props.defaultIsOpened || false,
isOpened: this.props.defaultIsOpened || false
};
static defaultProps = {
caretIcon: 'fa fa-angle-down',
caretIcon: 'fa fa-angle-down'
};
target: any;
@ -49,38 +49,41 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
e.preventDefault();
this.setState({
isOpened: !this.state.isOpened,
isOpened: !this.state.isOpened
});
}
open() {
this.setState({
isOpened: true,
isOpened: true
});
}
close() {
this.setState({
isOpened: false,
isOpened: false
});
}
renderOuter() {
const {
render,
buttons,
data,
popOverContainer,
classnames: cx,
classPrefix: ns,
children,
render,
buttons,
data,
popOverContainer,
classnames: cx,
classPrefix: ns,
children,
align,
closeOnClick,
closeOnOutside
} = this.props;
let body = (
<RootCloseWrapper disabled={!this.state.isOpened} onRootClose={closeOnOutside !== false ? this.close: noop}>
<RootCloseWrapper
disabled={!this.state.isOpened}
onRootClose={closeOnOutside !== false ? this.close : noop}
>
<ul className={cx('DropDown-menu')} onClick={closeOnClick ? this.close : noop}>
{children
? children
@ -97,7 +100,7 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
{render(`button/${index}`, {
type: 'button',
...button,
isMenuItem: true,
isMenuItem: true
})}
</li>
);
@ -146,7 +149,7 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
align,
iconOnly,
icon,
data,
data
} = this.props;
return (
@ -154,7 +157,7 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
className={cx('DropDown ', {
'DropDown--block': block,
'DropDown--alignRight': align === 'right',
'is-opened': this.state.isOpened,
'is-opened': this.state.isOpened
})}
ref={this.domRef}
>
@ -168,12 +171,12 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
{
'Button--block': block,
'Button--primary': primary,
'Button--iconOnly': iconOnly,
'Button--iconOnly': iconOnly
},
size ? `Button--${size}` : ''
)}
>
{icon ? (<i className={cx(icon, 'm-r-xs')} />) : null}
{icon ? <i className={cx(icon, 'm-r-xs')} /> : null}
{typeof label === 'string' ? filter(label, data) : label}
<i className={cx('DropDown-caret', caretIcon)} />
</button>
@ -186,6 +189,6 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
@Renderer({
test: /(^|\/)dropdown-button$/,
name: 'dropdown-button',
name: 'dropdown-button'
})
export class DropDownButtonRenderer extends DropDownButton {}

View File

@ -12,7 +12,7 @@ export interface EachProps extends RendererProps {
export default class Each extends React.Component<EachProps> {
static propsList: Array<string> = ['name', 'items', 'value'];
static defaultProps: Partial<EachProps> = {
className: '',
className: ''
};
render() {
@ -23,7 +23,7 @@ export default class Each extends React.Component<EachProps> {
? isObject(value)
? Object.keys(value).map(key => ({
key: key,
value: value[key],
value: value[key]
}))
: Array.isArray(value)
? value
@ -36,7 +36,7 @@ export default class Each extends React.Component<EachProps> {
? arr.map((item: any, index: number) =>
render(`item/${index}`, items, {
data: createObject(data, isObject(item) ? item : {[name]: item, item: item}),
key: index,
key: index
})
)
: null}
@ -47,6 +47,6 @@ export default class Each extends React.Component<EachProps> {
@Renderer({
test: /(^|\/)(?:repeat|each)$/,
name: 'each',
name: 'each'
})
export class EachRenderer extends Each {}

View File

@ -1,14 +1,10 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import { Schema } from '../../types';
import {FormItem, FormControlProps} from './Item';
import {Schema} from '../../types';
import {ComboStore, IComboStore} from '../../store/combo';
import { observer } from "mobx-react";
import {observer} from 'mobx-react';
import Combo from './Combo';
export interface ArrayProps extends FormControlProps {
placeholder?: string;
controls: Array<Schema>;
@ -19,37 +15,32 @@ export interface ArrayProps extends FormControlProps {
unique?: boolean;
};
store: IComboStore;
};
}
export default class ArrayControl extends React.Component<ArrayProps> {
comboInstance:any;
constructor(props:ArrayProps) {
comboInstance: any;
constructor(props: ArrayProps) {
super(props);
this.comboRef = this.comboRef.bind(this);
}
comboRef(ref:any) {
comboRef(ref: any) {
this.comboInstance = ref;
}
validate(args:Array<any>) {
validate(args: Array<any>) {
return this.comboInstance ? this.comboInstance.validate(...args) : null;
}
render() {
const {
items,
...rest
} = this.props;
const {items, ...rest} = this.props;
return (<Combo {...rest} controls={[items]} flat multiple multiLine={false} ref={this.comboRef} />);
return <Combo {...rest} controls={[items]} flat multiple multiLine={false} ref={this.comboRef} />;
}
}
@FormItem({
type: 'array',
storeType: ComboStore.name
})
export class ArrayControlRenderer extends ArrayControl {};
export class ArrayControlRenderer extends ArrayControl {}

View File

@ -1,25 +1,14 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import {Button} from '../../types';
export interface ButtonProps extends FormControlProps, Button {
};
export interface ButtonProps extends FormControlProps, Button {}
export class ButtonControl extends React.Component<ButtonProps, any> {
static defaultProps:Partial<ButtonProps> = {
}
static defaultProps: Partial<ButtonProps> = {};
render() {
const {
render,
type,
children,
...rest
} = this.props;
const {render, type, children, ...rest} = this.props;
return render('action', {
...rest,
@ -34,7 +23,7 @@ export class ButtonControl extends React.Component<ButtonProps, any> {
strictMode: false,
sizeMutable: false
})
export class ButtonControlRenderer extends ButtonControl {};
export class ButtonControlRenderer extends ButtonControl {}
@FormItem({
type: 'submit',
@ -42,12 +31,12 @@ export class ButtonControlRenderer extends ButtonControl {};
sizeMutable: false,
strictMode: false
})
export class SubmitControlRenderer extends ButtonControl {};
export class SubmitControlRenderer extends ButtonControl {}
@FormItem({
type: 'reset',
renderLabel: false,
strictMode: false,
sizeMutable: false,
sizeMutable: false
})
export class ResetControlRenderer extends ButtonControl {};
export class ResetControlRenderer extends ButtonControl {}

View File

@ -1,15 +1,9 @@
import React from 'react';
import cx from 'classnames';
import {
OptionsControl,
OptionsControlProps,
Option
} from './Options';
import {
Button
} from '../../types';
import { getLevelFromClassName, autobind, isEmpty} from '../../utils/helper';
import { dataMapping } from '../../utils/tpl-builtin';
import {OptionsControl, OptionsControlProps, Option} from './Options';
import {Button} from '../../types';
import {getLevelFromClassName, autobind, isEmpty} from '../../utils/helper';
import {dataMapping} from '../../utils/tpl-builtin';
export interface ButtonGroupProps extends OptionsControlProps {
buttons?: Array<Button>;
@ -18,28 +12,23 @@ export interface ButtonGroupProps extends OptionsControlProps {
btnClassName: string;
btnActiveClassName: string;
vertical?: boolean;
};
}
export default class ButtonGroupControl extends React.Component<ButtonGroupProps, any> {
static defaultProps:Partial<ButtonGroupProps> = {
static defaultProps: Partial<ButtonGroupProps> = {
btnLevel: 'default',
btnActiveLevel: 'primary',
clearable: false,
vertical: false
}
};
@autobind
handleToggle(option: Option) {
const {
onToggle,
multiple,
autoFill,
onBulkChange
} = this.props;
const {onToggle, multiple, autoFill, onBulkChange} = this.props;
const sendTo = !multiple && autoFill && !isEmpty(autoFill) && dataMapping(autoFill, option as Option);
sendTo && onBulkChange(sendTo);
onToggle(option)
onToggle(option);
}
reload() {
@ -66,7 +55,7 @@ export default class ButtonGroupControl extends React.Component<ButtonGroupProps
vertical
} = props;
let body:Array<React.ReactNode> = [];
let body: Array<React.ReactNode> = [];
let btnLevel = props.btnLevel;
let btnActiveLevel = props.btnActiveLevel;
@ -76,42 +65,58 @@ export default class ButtonGroupControl extends React.Component<ButtonGroupProps
if (options && options.length) {
body = options.map((option, key) => {
const active = !!~selectedOptions.indexOf(option);
return render(`option/${key}`, {
label: option.label,
icon: option.icon,
size: option.size || size,
type: 'button',
block: block,
}, {
key: key,
active,
level: (active ? btnActiveLevel : '') || option.level || btnLevel,
className: cx(option.className, btnClassName),
disabled: option.disabled || disabled,
onClick: (e:React.UIEvent<any>) => {
this.handleToggle(option);
e.preventDefault(); // 禁止 onAction 触发
return render(
`option/${key}`,
{
label: option.label,
icon: option.icon,
size: option.size || size,
type: 'button',
block: block
},
{
key: key,
active,
level: (active ? btnActiveLevel : '') || option.level || btnLevel,
className: cx(option.className, btnClassName),
disabled: option.disabled || disabled,
onClick: (e: React.UIEvent<any>) => {
this.handleToggle(option);
e.preventDefault(); // 禁止 onAction 触发
}
}
});
);
});
} else if (buttons) {
body = buttons.map((button, key) => render(`button/${key}`, {
size: size,
block: block,
activeLevel: btnActiveLevel,
...button
}, {
key,
className: cx(button.className, btnClassName),
}));
body = buttons.map((button, key) =>
render(
`button/${key}`,
{
size: size,
block: block,
activeLevel: btnActiveLevel,
...button
},
{
key,
className: cx(button.className, btnClassName)
}
)
);
}
return (
<div className={cx(`ButtonGroup`, {
'ButtonGroup--block': block,
'ButtonGroup--vertical': vertical,
[`ButtonGroup--${size}`]: size
}, className)}>
<div
className={cx(
`ButtonGroup`,
{
'ButtonGroup--block': block,
'ButtonGroup--vertical': vertical,
[`ButtonGroup--${size}`]: size
},
className
)}
>
{body.length ? body : <span className={`${ns}ButtonGroup-placeholder`}>{placeholder}</span>}
</div>
);
@ -124,22 +129,13 @@ export default class ButtonGroupControl extends React.Component<ButtonGroupProps
})
export class ButtonGroupControlRenderer extends ButtonGroupControl {
render() {
const {
className,
classnames: cx,
...rest
} = this.props;
const {className, classnames: cx, ...rest} = this.props;
const body = super.render({
...rest,
classnames: cx
});
return (
<div className={cx('ButtonGroupControl', className)}>
{body}
</div>
)
return <div className={cx('ButtonGroupControl', className)}>{body}</div>;
}
};
}

View File

@ -1,36 +1,27 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import {
Button
} from '../../types';
import {Button} from '../../types';
export interface ButtonToolbarProps extends FormControlProps {
buttons?: Array<Button>;
};
}
export class ButtonToolbarControl extends React.Component<ButtonToolbarProps> {
static defaultProps = {
}
static defaultProps = {};
render() {
const {
render,
className,
classPrefix: ns,
buttons
} = this.props;
const {render, className, classPrefix: ns, buttons} = this.props;
return (
<div className={cx(`${ns}ButtonToolbar`, className)}>
{Array.isArray(buttons) ? buttons.map((button, key) => render(`button/${key}`, button, {
key: key
})) : null}
{Array.isArray(buttons)
? buttons.map((button, key) =>
render(`button/${key}`, button, {
key: key
})
)
: null}
</div>
);
}
@ -41,5 +32,4 @@ export class ButtonToolbarControl extends React.Component<ButtonToolbarProps> {
sizeMutable: false,
strictMode: false // data 变化也更新
})
export class ButtonToolbarRenderer extends ButtonToolbarControl {};
export class ButtonToolbarRenderer extends ButtonToolbarControl {}

View File

@ -1,9 +1,6 @@
import React from 'react';
import cx from 'classnames';
import {
OptionsControl,
OptionsControlProps,
Option} from './Options';
import {OptionsControl, OptionsControlProps, Option} from './Options';
import Select from '../../components/Select';
import {Api} from '../../types';
import {isEffectiveApi} from '../../utils/api';
@ -11,7 +8,7 @@ import {isEffectiveApi} from '../../utils/api';
export interface ChainedSelectProps extends OptionsControlProps {
autoComplete?: Api;
searchable?: boolean;
};
}
export interface SelectState {
stack: Array<{
@ -23,32 +20,29 @@ export interface SelectState {
}
export default class ChainedSelectControl extends React.Component<ChainedSelectProps, SelectState> {
static defaultProps:Partial<ChainedSelectProps> = {
static defaultProps: Partial<ChainedSelectProps> = {
clearable: false,
searchable: false,
multiple: true
}
};
state:SelectState = {
state: SelectState = {
stack: []
};
constructor(props:ChainedSelectProps) {
constructor(props: ChainedSelectProps) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.loadMore = this.loadMore.bind(this);
}
componentDidMount() {
const {
formInited
} = this.props;
formInited ? this.loadMore() : this.props.addHook(this.loadMore, 'init');
const {formInited} = this.props;
formInited ? this.loadMore() : this.props.addHook(this.loadMore, 'init');
}
componentWillReceiveProps(nextProps:ChainedSelectProps) {
componentWillReceiveProps(nextProps: ChainedSelectProps) {
const props = this.props;
if (props.options !== nextProps.options) {
@ -58,7 +52,7 @@ export default class ChainedSelectControl extends React.Component<ChainedSelectP
}
}
componentDidUpdate(prevProps:ChainedSelectProps) {
componentDidUpdate(prevProps: ChainedSelectProps) {
const props = this.props;
if (props.value !== prevProps.value) {
@ -67,21 +61,20 @@ export default class ChainedSelectControl extends React.Component<ChainedSelectP
}
loadMore() {
const {
value,
delimiter,
onChange,
joinValues,
extractValue,
source,
data,
env
} = this.props;
const {value, delimiter, onChange, joinValues, extractValue, source, data, env} = this.props;
const arr = Array.isArray(value) ? value.concat() : value && typeof value === 'string' ? value.split(delimiter || ',') : [];
const arr = Array.isArray(value)
? value.concat()
: value && typeof value === 'string'
? value.split(delimiter || ',')
: [];
let idx = 0;
let len = this.state.stack.length;
while (idx < len && arr[idx] && this.state.stack[idx].parentId == ((joinValues || extractValue) ? arr[idx] : arr[idx].value)) {
while (
idx < len &&
arr[idx] &&
this.state.stack[idx].parentId == (joinValues || extractValue ? arr[idx] : arr[idx].value)
) {
idx++;
}
@ -89,7 +82,7 @@ export default class ChainedSelectControl extends React.Component<ChainedSelectP
return;
}
const parentId = (joinValues || extractValue) ? arr[idx] : arr[idx].value;
const parentId = joinValues || extractValue ? arr[idx] : arr[idx].value;
const stack = this.state.stack.concat();
stack.splice(idx, stack.length - idx);
stack.push({
@ -98,57 +91,60 @@ export default class ChainedSelectControl extends React.Component<ChainedSelectP
options: []
});
this.setState({
stack
}, () => {
env
.fetcher(source as Api, {
this.setState(
{
stack
},
() => {
env.fetcher(source as Api, {
...data,
value: arr,
level: idx + 1,
parentId,
parent: arr[idx]
})
.then(ret => {
const stack = this.state.stack.concat();
const remoteValue = ret.data ? ret.data.value : undefined;
let options = ret.data && (ret.data as any).options || ret.data;
stack.splice(idx, stack.length - idx);
.then(ret => {
const stack = this.state.stack.concat();
const remoteValue = ret.data ? ret.data.value : undefined;
let options = (ret.data && (ret.data as any).options) || ret.data;
if (typeof remoteValue !== 'undefined') {
arr.splice(idx + 1, value.length - idx - 1);
arr.push(remoteValue);
onChange(joinValues ? arr.join(delimiter || ',') : arr);
}
stack.splice(idx, stack.length - idx);
stack.push({
options,
parentId,
loading: false,
visible: !!options
if (typeof remoteValue !== 'undefined') {
arr.splice(idx + 1, value.length - idx - 1);
arr.push(remoteValue);
onChange(joinValues ? arr.join(delimiter || ',') : arr);
}
stack.push({
options,
parentId,
loading: false,
visible: !!options
});
this.setState(
{
stack: stack
},
this.loadMore
);
})
.catch(e => {
env.notify('error', e.message);
});
this.setState({
stack: stack
}, this.loadMore);
})
.catch(e => {
env.notify('error', e.message);
})
});
}
);
}
handleChange(index:number, currentValue:any) {
const {
value,
delimiter,
onChange,
joinValues,
extractValue
} = this.props;
handleChange(index: number, currentValue: any) {
const {value, delimiter, onChange, joinValues, extractValue} = this.props;
const arr = Array.isArray(value) ? value.concat() : value && typeof value === 'string' ? value.split(delimiter || ',') : [];
const arr = Array.isArray(value)
? value.concat()
: value && typeof value === 'string'
? value.split(delimiter || ',')
: [];
arr.splice(index, arr.length - index);
arr.push(joinValues ? currentValue.value : currentValue);
@ -174,7 +170,11 @@ export default class ChainedSelectControl extends React.Component<ChainedSelectP
multiple,
...rest
} = this.props;
const arr = Array.isArray(value) ? value.concat() : value && typeof value === 'string' ? value.split(delimiter || ',') : [];
const arr = Array.isArray(value)
? value.concat()
: value && typeof value === 'string'
? value.split(delimiter || ',')
: [];
return (
<div className={cx(`${ns}ChainedSelectControl`, className)}>
@ -189,22 +189,20 @@ export default class ChainedSelectControl extends React.Component<ChainedSelectP
inline
/>
{this.state.stack.map(({
options,
loading,
visible
}, index) => visible === false ? null : (
<Select
{...rest}
classPrefix={ns}
key={`x-${index + 1}`}
options={options}
value={arr[index + 1]}
onChange={this.handleChange.bind(this, index + 1)}
loading={loading}
inline
/>
))}
{this.state.stack.map(({options, loading, visible}, index) =>
visible === false ? null : (
<Select
{...rest}
classPrefix={ns}
key={`x-${index + 1}`}
options={options}
value={arr[index + 1]}
onChange={this.handleChange.bind(this, index + 1)}
loading={loading}
inline
/>
)
)}
</div>
);
}
@ -214,5 +212,4 @@ export default class ChainedSelectControl extends React.Component<ChainedSelectP
type: 'chained-select',
sizeMutable: false
})
export class ChainedSelectControlRenderer extends ChainedSelectControl {};
export class ChainedSelectControlRenderer extends ChainedSelectControl {}

View File

@ -1,8 +1,5 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import Checkbox from '../../components/Checkbox';
@ -10,13 +7,13 @@ export interface CheckboxProps extends FormControlProps {
option?: string;
trueValue?: any;
falseValue?: any;
};
}
export default class CheckboxControl extends React.Component<CheckboxProps, any> {
static defaultProps:Partial<CheckboxProps> = {
static defaultProps: Partial<CheckboxProps> = {
trueValue: true,
falseValue: false
}
};
render() {
const {
className,
@ -39,7 +36,7 @@ export default class CheckboxControl extends React.Component<CheckboxProps, any>
falseValue={falseValue}
classPrefix={ns}
disabled={disabled}
onChange={(value:any) => onChange(value)}
onChange={(value: any) => onChange(value)}
>
{option ? render('option', option) : null}
</Checkbox>
@ -52,5 +49,4 @@ export default class CheckboxControl extends React.Component<CheckboxProps, any>
type: 'checkbox',
sizeMutable: false
})
export class CheckboxControlRenderer extends CheckboxControl {};
export class CheckboxControlRenderer extends CheckboxControl {}

View File

@ -1,9 +1,5 @@
import React from 'react';
import {
OptionsControl,
OptionsControlProps,
Option
} from './Options';
import {OptionsControl, OptionsControlProps, Option} from './Options';
import cx from 'classnames';
import Checkbox from '../../components/Checkbox';
import chunk = require('lodash/chunk');
@ -13,20 +9,17 @@ export interface CheckboxesProps extends OptionsControlProps {
disabled?: boolean;
itemClassName?: string;
columnsCount?: number;
};
}
export default class CheckboxesControl extends React.Component<CheckboxesProps, any> {
static defaultProps:Partial<CheckboxesProps> = {
static defaultProps: Partial<CheckboxesProps> = {
columnsCount: 1,
multiple: true,
placeholder: '暂无选项'
}
};
componentDidMount() {
const {
defaultCheckAll,
onToggleAll
} = this.props;
const {defaultCheckAll, onToggleAll} = this.props;
defaultCheckAll && onToggleAll();
}
@ -36,36 +29,26 @@ export default class CheckboxesControl extends React.Component<CheckboxesProps,
reload && reload();
}
renderGroup(option:Option, index:number) {
const {
classnames: cx
} = this.props;
renderGroup(option: Option, index: number) {
const {classnames: cx} = this.props;
return (
<div key={index} className={cx("CheckboxesControl-group", option.className)}>
<label className={cx("CheckboxesControl-groupLabel", option.labelClassName)}>{option.label}</label>
<div key={index} className={cx('CheckboxesControl-group', option.className)}>
<label className={cx('CheckboxesControl-groupLabel', option.labelClassName)}>{option.label}</label>
{
option.children && option.children.length
? option.children.map((option, index) => this.renderItem(option, index))
: null
}
{option.children && option.children.length
? option.children.map((option, index) => this.renderItem(option, index))
: null}
</div>
);
}
renderItem(option:Option, index:number) {
renderItem(option: Option, index: number) {
if (option.children) {
return this.renderGroup(option, index);
}
const {
itemClassName,
onToggle,
selectedOptions,
disabled,
inline
} = this.props;
const {itemClassName, onToggle, selectedOptions, disabled, inline} = this.props;
return (
<Checkbox
@ -97,7 +80,7 @@ export default class CheckboxesControl extends React.Component<CheckboxesProps,
itemClassName
} = this.props;
let body:Array<React.ReactNode> = [];
let body: Array<React.ReactNode> = [];
if (options && options.length) {
body = options.map((option, key) => this.renderItem(option, key));
@ -120,12 +103,14 @@ export default class CheckboxesControl extends React.Component<CheckboxesProps,
}
if (!inline && (columnsCount as number) > 1) {
let weight = 12/(columnsCount as number);
let weight = 12 / (columnsCount as number);
let cellClassName = `Grid-col--sm${weight === Math.round(weight) ? weight : ''}`;
body = chunk(body, columnsCount).map((group, groupIndex) => (
<div className={cx('Grid')} key={groupIndex}>
{Array.from({length: columnsCount as number}).map((_, index) => (
<div key={index} className={cx(cellClassName)}>{group[index]}</div>
<div key={index} className={cx(cellClassName)}>
{group[index]}
</div>
))}
</div>
));
@ -133,9 +118,7 @@ export default class CheckboxesControl extends React.Component<CheckboxesProps,
return (
<div className={cx(`CheckboxesControl`, className)}>
{body && body.length ? body : (
<span className={`Form-placeholder`}>{placeholder}</span>
)}
{body && body.length ? body : <span className={`Form-placeholder`}>{placeholder}</span>}
</div>
);
}
@ -145,5 +128,4 @@ export default class CheckboxesControl extends React.Component<CheckboxesProps,
type: 'checkboxes',
sizeMutable: false
})
export class CheckboxesControlRenderer extends CheckboxesControl {};
export class CheckboxesControlRenderer extends CheckboxesControl {}

View File

@ -1,22 +1,14 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import db, {
province,
city,
district
} from './CityDB';
import { ClassNamesFn, themeable } from '../../theme';
import { Select } from '../../components';
import { autobind } from '../../utils/helper';
import { Option } from './Options';
import {FormItem, FormControlProps} from './Item';
import db, {province, city, district} from './CityDB';
import {ClassNamesFn, themeable} from '../../theme';
import {Select} from '../../components';
import {autobind} from '../../utils/helper';
import {Option} from './Options';
export interface CityPickerProps {
value:any;
onChange: (value:any) => void;
value: any;
onChange: (value: any) => void;
extractValue: boolean;
joinValues?: boolean;
delimiter: string;
@ -27,7 +19,7 @@ export interface CityPickerProps {
allowCity: boolean;
allowDistrict: boolean;
// allowStreet: boolean;
};
}
export interface CityPickerState {
code: number;
@ -38,7 +30,7 @@ export interface CityPickerState {
district: string;
districtCode: number;
street: string;
};
}
export class CityPicker extends React.Component<CityPickerProps, CityPickerState> {
static defaultProps = {
@ -46,7 +38,7 @@ export class CityPicker extends React.Component<CityPickerProps, CityPickerState
extractValue: true,
delimiter: ',',
allowCity: true,
allowDistrict: true,
allowDistrict: true
// allowStreet: false
};
@ -65,60 +57,68 @@ export class CityPicker extends React.Component<CityPickerProps, CityPickerState
this.syncIn();
}
componentWillReceiveProps(nextProps:CityPickerProps) {
componentWillReceiveProps(nextProps: CityPickerProps) {
const props = this.props;
if (props.value !== nextProps.value) {
this.syncIn(nextProps);
}
}
@autobind
handleProvinceChange(option:Option) {
this.setState({
province: option.label as string,
provinceCode: option.value as number,
city: '',
cityCode: 0,
district: '',
districtCode: 0,
street: '',
code: option.value
}, this.syncOut);
@autobind
handleProvinceChange(option: Option) {
this.setState(
{
province: option.label as string,
provinceCode: option.value as number,
city: '',
cityCode: 0,
district: '',
districtCode: 0,
street: '',
code: option.value
},
this.syncOut
);
}
@autobind
handleCityChange(option:Option) {
handleCityChange(option: Option) {
if (option.value % 100) {
return this.handleDistrictChange(option, {
cityCode: option.value as number
});
}
this.setState({
city: option.label as string,
cityCode: option.value as number,
district: '',
districtCode: 0,
street: '',
code: option.value
}, this.syncOut);
this.setState(
{
city: option.label as string,
cityCode: option.value as number,
district: '',
districtCode: 0,
street: '',
code: option.value
},
this.syncOut
);
}
@autobind
handleDistrictChange(option:Option, otherStates:Partial<CityPickerState> = {}) {
this.setState({
...otherStates as any,
district: option.label as string,
districtCode: option.value as number,
street: '',
code: option.value as number
}, this.syncOut);
handleDistrictChange(option: Option, otherStates: Partial<CityPickerState> = {}) {
this.setState(
{
...(otherStates as any),
district: option.label as string,
districtCode: option.value as number,
street: '',
code: option.value as number
},
this.syncOut
);
}
@autobind
handleStreetChange(e:React.ChangeEvent<HTMLInputElement>) {
handleStreetChange(e: React.ChangeEvent<HTMLInputElement>) {
this.setState({
street: e.currentTarget.value
});
@ -131,10 +131,7 @@ export class CityPicker extends React.Component<CityPickerProps, CityPickerState
@autobind
syncIn(props = this.props) {
const {
value,
delimiter
} = props;
const {value, delimiter} = props;
const state = {
code: 0,
@ -147,10 +144,11 @@ export class CityPicker extends React.Component<CityPickerProps, CityPickerState
street: ''
};
let code = value && value.code
|| typeof value === "number" && value
|| typeof value === "string" && /(\d{6})/.test(value) && RegExp.$1;
let code =
(value && value.code) ||
(typeof value === 'number' && value) ||
(typeof value === 'string' && /(\d{6})/.test(value) && RegExp.$1);
if (code && db[code]) {
code = parseInt(code, 10);
state.code = code;
@ -160,7 +158,7 @@ export class CityPicker extends React.Component<CityPickerProps, CityPickerState
state.provinceCode = provinceCode;
state.province = db[provinceCode];
}
const cityCode = code - (code % 100);
if (db[cityCode]) {
state.cityCode = cityCode;
@ -177,8 +175,8 @@ export class CityPicker extends React.Component<CityPickerProps, CityPickerState
if (value && value.street) {
state.street = value.street;
} else if (typeof value === "string" && ~value.indexOf(delimiter)) {
state.street = value.slice(value.indexOf(delimiter) + delimiter.length)
} else if (typeof value === 'string' && ~value.indexOf(delimiter)) {
state.street = value.slice(value.indexOf(delimiter) + delimiter.length);
}
this.setState(state);
@ -198,18 +196,18 @@ export class CityPicker extends React.Component<CityPickerProps, CityPickerState
code,
province,
city,
district,
district
// street
} = this.state;
if (typeof extractValue === "undefined" ? joinValues : extractValue ) {
if (typeof extractValue === 'undefined' ? joinValues : extractValue) {
code ? onChange(/*allowStreet && street ? [code, street].join(delimiter) :*/ String(code)) : onChange('');
} else {
onChange({
code,
province,
city,
district,
district
// street
});
}
@ -221,16 +219,11 @@ export class CityPicker extends React.Component<CityPickerProps, CityPickerState
className,
disabled,
allowCity,
allowDistrict,
allowDistrict
// allowStreet
} = this.props;
const {
provinceCode,
cityCode,
districtCode,
street
} = this.state;
const {provinceCode, cityCode, districtCode, street} = this.state;
return (
<div className={cx('CityPicker', className)}>
@ -294,28 +287,26 @@ export class CityPicker extends React.Component<CityPickerProps, CityPickerState
const ThemedCity = themeable(CityPicker);
export default ThemedCity;
export interface LocationControlProps extends FormControlProps {
allowCity?: boolean;
allowDistrict?: boolean;
extractValue?: boolean;
joinValues?: boolean;
// allowStreet?: boolean;
};
}
export class LocationControl extends React.Component<LocationControlProps> {
render() {
const {
value,
value,
onChange,
allowCity,
allowDistrict,
extractValue,
joinValues,
joinValues
// allowStreet
} = this.props;
return (
<ThemedCity
<ThemedCity
value={value}
onChange={onChange}
allowCity={allowCity}
@ -332,5 +323,4 @@ export class LocationControl extends React.Component<LocationControlProps> {
type: 'city',
sizeMutable: false
})
export class CheckboxControlRenderer extends LocationControl {};
export class CheckboxControlRenderer extends LocationControl {}

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,5 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import ColorPicker from '../../components/ColorPicker';
@ -10,43 +7,35 @@ export interface ColorProps extends FormControlProps {
placeholder?: string;
format?: string;
timeConstrainst?: object;
closeOnSelect?:boolean;
closeOnSelect?: boolean;
presetColors?: string[];
};
}
export interface ColorControlState {
open: boolean;
};
}
export default class ColorControl extends React.PureComponent<ColorProps, ColorControlState> {
static defaultProps:Partial<ColorProps> = {
static defaultProps: Partial<ColorProps> = {
format: 'hex',
clearable: true
};
state:ColorControlState = {
state: ColorControlState = {
open: false
};
render() {
const {
className,
classPrefix: ns,
...rest
} = this.props;
const {className, classPrefix: ns, ...rest} = this.props;
return (
<div className={cx(`${ns}ColorControl`, className)}>
<ColorPicker
classPrefix={ns}
{...rest}
/>
<ColorPicker classPrefix={ns} {...rest} />
</div>
);
}
}
@FormItem({
type: 'color',
type: 'color'
})
export class ColorControlRenderer extends ColorControl {};
export class ColorControlRenderer extends ColorControl {}

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,10 @@
import React from 'react';
import {
Renderer
} from '../../factory';
import {Renderer} from '../../factory';
import cx from 'classnames';
import Container from '../Container';
import FormItem, { FormControlProps } from './Item';
import FormItem, {FormControlProps} from './Item';
export interface ContainerProps extends FormControlProps {};
export interface ContainerProps extends FormControlProps {}
@FormItem({
type: 'container',
@ -14,7 +12,7 @@ export interface ContainerProps extends FormControlProps {};
sizeMutable: false
})
export class ContainerControlRenderer extends Container<ContainerProps> {
renderBody():JSX.Element | null {
renderBody(): JSX.Element | null {
const {
renderFormItems,
body,
@ -32,7 +30,7 @@ export class ContainerControlRenderer extends Container<ContainerProps> {
} = this.props;
if (!body && (controls || tabs || fieldSet)) {
let props:any = {
let props: any = {
store,
data: store.data,
render
@ -42,11 +40,15 @@ export class ContainerControlRenderer extends Container<ContainerProps> {
return (
<div className={cx(`${ns}Form--${props.mode || formMode || 'normal'}`, bodyClassName)}>
{renderFormItems({
controls,
tabs,
fieldSet
}, ($path as string).replace(/^.*form\//, ''), props)}
{renderFormItems(
{
controls,
tabs,
fieldSet
},
($path as string).replace(/^.*form\//, ''),
props
)}
</div>
);
}

View File

@ -3,26 +3,12 @@ import PropTypes from 'prop-types';
import {IFormStore, IFormItemStore} from '../../store/form';
import debouce = require('lodash/debounce');
import {
RendererProps,
Renderer
} from '../../factory';
import {
ComboStore,
IComboStore,
IUniqueGroup
} from '../../store/combo';
import {
anyChanged,
promisify,
isObject,
getVariable
} from '../../utils/helper';
import { Schema } from '../../types';
import { IIRendererStore } from '../../store';
import { ScopedContext, IScopedContext } from '../../Scoped';
import {RendererProps, Renderer} from '../../factory';
import {ComboStore, IComboStore, IUniqueGroup} from '../../store/combo';
import {anyChanged, promisify, isObject, getVariable} from '../../utils/helper';
import {Schema} from '../../types';
import {IIRendererStore} from '../../store';
import {ScopedContext, IScopedContext} from '../../Scoped';
export interface FormControlProps extends RendererProps {
control: {
@ -30,8 +16,8 @@ export interface FormControlProps extends RendererProps {
name?: string;
value?: any;
required?: boolean;
validations: string | {[propsName:string]: any};
validationErrors: {[propsName:string]: any};
validations: string | {[propsName: string]: any};
validationErrors: {[propsName: string]: any};
validateOnChange: boolean;
multiple?: boolean;
delimiter?: string;
@ -40,9 +26,9 @@ export interface FormControlProps extends RendererProps {
valueField?: string;
labelField?: string;
unique?: boolean;
pipeIn?: (value:any, data:any) => any;
pipeOut?: (value:any, originValue:any, data:any) => any;
validate?: (value:any, values:any) => any;
pipeIn?: (value: any, data: any) => any;
pipeOut?: (value: any, originValue: any, data: any) => any;
validate?: (value: any, values: any) => any;
} & Schema;
formStore: IFormStore;
store: IIRendererStore;
@ -51,15 +37,14 @@ export interface FormControlProps extends RendererProps {
}
export default class FormControl extends React.Component<FormControlProps, any> {
public model:IFormItemStore | undefined;
control:any;
public model: IFormItemStore | undefined;
control: any;
hook?: () => any;
hook2?: () => any;
static defaultProps:Partial<FormControlProps> = {
};
static defaultProps: Partial<FormControlProps> = {};
lazyValidate:Function;
lazyValidate: Function;
componentWillMount() {
const {
formStore: form,
@ -77,7 +62,7 @@ export default class FormControl extends React.Component<FormControlProps, any>
valueField,
labelField,
joinValues,
extractValue,
extractValue
}
} = this.props;
@ -110,7 +95,7 @@ export default class FormControl extends React.Component<FormControlProps, any>
valueField,
labelField,
joinValues,
extractValue,
extractValue
});
if (this.model.unique && form.parentStore && form.parentStore.storeType === ComboStore.name) {
@ -123,17 +108,14 @@ export default class FormControl extends React.Component<FormControlProps, any>
const {
store,
formStore: form,
control: {
name,
validate,
},
control: {name, validate},
addHook
} = this.props;
if (name && form !== store) {
const value = getVariable(store.data, name);
if (typeof value !== 'undefined' && value !== this.getValue()) {
this.handleChange(value)
this.handleChange(value);
}
}
@ -142,23 +124,21 @@ export default class FormControl extends React.Component<FormControlProps, any>
let finalValidate = promisify(validate.bind(formItem));
this.hook2 = function() {
formItem.clearError('control:valdiate');
return finalValidate(form.data, formItem.value)
.then((ret:any) => {
if ((typeof ret === 'string' || Array.isArray(ret)) && ret) {
formItem.addError(ret, 'control:valdiate');
}
});
}
return finalValidate(form.data, formItem.value).then((ret: any) => {
if ((typeof ret === 'string' || Array.isArray(ret)) && ret) {
formItem.addError(ret, 'control:valdiate');
}
});
};
addHook(this.hook2);
}
}
componentWillReceiveProps(nextProps:FormControlProps) {
componentWillReceiveProps(nextProps: FormControlProps) {
const props = this.props;
const form = nextProps.formStore;
if (!nextProps.control.name) {
// 把 name 删了, 对 model 做清理
this.model && this.disposeModel();
this.model = undefined;
@ -187,21 +167,25 @@ export default class FormControl extends React.Component<FormControlProps, any>
}
if (
this.model
&& anyChanged([
'id',
'validations',
'validationErrors',
'value',
'required',
'unique',
'multiple',
'delimiter',
'valueField',
'labelField',
'joinValues',
'extractValue',
], props.control, nextProps.control)
this.model &&
anyChanged(
[
'id',
'validations',
'validationErrors',
'value',
'required',
'unique',
'multiple',
'delimiter',
'valueField',
'labelField',
'joinValues',
'extractValue'
],
props.control,
nextProps.control
)
) {
this.model.config({
required: nextProps.control.required,
@ -220,14 +204,12 @@ export default class FormControl extends React.Component<FormControlProps, any>
}
}
componentDidUpdate(prevProps:FormControlProps) {
componentDidUpdate(prevProps: FormControlProps) {
const {
store,
formStore: form,
data,
control: {
name
}
control: {name}
} = this.props;
if (!name) {
@ -235,8 +217,12 @@ export default class FormControl extends React.Component<FormControlProps, any>
}
// form 里面部分塞 service 的用法
let value:any;
if (form !== store && data !== prevProps.data && (value = getVariable(data as any, name)) !== getVariable(prevProps.data, name)) {
let value: any;
if (
form !== store &&
data !== prevProps.data &&
(value = getVariable(data as any, name)) !== getVariable(prevProps.data, name)
) {
this.handleChange(value);
}
}
@ -248,24 +234,18 @@ export default class FormControl extends React.Component<FormControlProps, any>
}
disposeModel() {
const {
formStore: form
} = this.props;
const {formStore: form} = this.props;
if (this.model && this.model.unique && form.parentStore && form.parentStore.storeType === ComboStore.name) {
const combo = form.parentStore as IComboStore;
combo.unBindUniuqueItem(this.model);
}
this.model && form.unRegistryItem(this.model);
}
controlRef(control:any) {
const {
addHook,
removeHook,
formStore: form
} = this.props;
controlRef(control: any) {
const {addHook, removeHook, formStore: form} = this.props;
// 因为 control 有可能被 n 层 hoc 包裹。
while (control && control.getWrappedInstance) {
@ -278,13 +258,12 @@ export default class FormControl extends React.Component<FormControlProps, any>
this.hook = function() {
formItem.clearError('component:valdiate');
return validate(form.data, formItem.value)
.then(ret => {
if ((typeof ret === 'string' || Array.isArray(ret)) && ret) {
formItem.setError(ret, 'component:valdiate');
}
});
}
return validate(form.data, formItem.value).then(ret => {
if ((typeof ret === 'string' || Array.isArray(ret)) && ret) {
formItem.setError(ret, 'component:valdiate');
}
});
};
addHook(this.hook);
} else if (!control && this.hook) {
removeHook(this.hook);
@ -295,9 +274,7 @@ export default class FormControl extends React.Component<FormControlProps, any>
}
validate() {
const {
formStore: form,
} = this.props;
const {formStore: form} = this.props;
if (this.model) {
if (this.model.unique && form.parentStore && form.parentStore.storeType === ComboStore.name) {
@ -306,23 +283,16 @@ export default class FormControl extends React.Component<FormControlProps, any>
group.items.forEach(item => item.validate());
} else {
this.model.validate(this.hook);
form.getItemsByName(this.model.name)
.forEach(item => item !== this.model && item.validate())
form.getItemsByName(this.model.name).forEach(item => item !== this.model && item.validate());
}
}
}
handleChange(value:any, submitOnChange:boolean = this.props.control.submitOnChange) {
handleChange(value: any, submitOnChange: boolean = this.props.control.submitOnChange) {
const {
formStore: form,
onChange,
control: {
validateOnChange,
name,
pipeOut,
onChange: onFormItemChange,
type
}
control: {validateOnChange, name, pipeOut, onChange: onFormItemChange, type}
} = this.props;
// todo 以后想办法不要強耦合类型。
@ -335,14 +305,14 @@ export default class FormControl extends React.Component<FormControlProps, any>
if (pipeOut) {
value = pipeOut(value, oldValue, form.data);
}
if (oldValue === value) {
return;
}
this.model.changeValue(value);
if (validateOnChange === true || validateOnChange !== false && (form.submited || this.model.validated)) {
if (validateOnChange === true || (validateOnChange !== false && (form.submited || this.model.validated))) {
this.lazyValidate();
} else if (validateOnChange === false && !this.model.valid) {
this.model.reset();
@ -352,12 +322,10 @@ export default class FormControl extends React.Component<FormControlProps, any>
onChange && onChange(value, name, submitOnChange === true);
}
handleBlur(e:any) {
handleBlur(e: any) {
const {
onBlur,
control: {
validateOnBlur
}
control: {validateOnBlur}
} = this.props;
if (validateOnBlur && this.model) {
@ -367,18 +335,14 @@ export default class FormControl extends React.Component<FormControlProps, any>
onBlur && onBlur(e);
}
handleBulkChange(values:any, submitOnChange:boolean = this.props.control.submitOnChange) {
handleBulkChange(values: any, submitOnChange: boolean = this.props.control.submitOnChange) {
const {
formStore: form,
onChange,
control: {
validateOnChange,
type
},
control: {validateOnChange, type},
onBulkChange
} = this.props;
if (!isObject(values)) {
return;
} else if (!this.model || ~['service'].indexOf(type)) {
@ -386,7 +350,8 @@ export default class FormControl extends React.Component<FormControlProps, any>
return;
}
let lastKey:string = '', lastValue:any;
let lastKey: string = '',
lastValue: any;
Object.keys(values).forEach(key => {
const value = values[key];
lastKey = key;
@ -397,7 +362,7 @@ export default class FormControl extends React.Component<FormControlProps, any>
if (!lastKey) {
return;
}
form.setValues(values);
if (validateOnChange !== false && (form.submited || this.model.validated)) {
@ -407,7 +372,7 @@ export default class FormControl extends React.Component<FormControlProps, any>
onChange && onChange(lastValue, lastKey, submitOnChange === true);
}
setPrinstineValue(value:any) {
setPrinstineValue(value: any) {
if (!this.model) {
return;
}
@ -416,14 +381,11 @@ export default class FormControl extends React.Component<FormControlProps, any>
}
getValue() {
const {
control,
formStore: form
} = this.props;
const {control, formStore: form} = this.props;
const model = this.model;
// let value:any = model ? (typeof model.value === 'undefined' ? '' : model.value) : (control.value || '');
let value:any = model ? model.value : control.value;
let value: any = model ? model.value : control.value;
if (control.pipeIn) {
value = control.pipeIn(value, form.data);
@ -433,11 +395,9 @@ export default class FormControl extends React.Component<FormControlProps, any>
}
// 兼容老版本用法,新版本直接用 onChange 就可以。
setValue(value:any, key?:string) {
setValue(value: any, key?: string) {
const {
control: {
name
}
control: {name}
} = this.props;
if (!key || key === name) {
@ -452,11 +412,7 @@ export default class FormControl extends React.Component<FormControlProps, any>
render() {
const {
render,
control: {
pipeIn,
pipeOut,
...control
},
control: {pipeIn, pipeOut, ...control},
formMode,
controlWidth,
type,
@ -493,14 +449,14 @@ export default class FormControl extends React.Component<FormControlProps, any>
}
@Renderer({
test: (path:string) => /(^|\/)form(?:\/.*)?\/control$/i.test(path) && !/\/control\/control$/i.test(path),
name: "control"
test: (path: string) => /(^|\/)form(?:\/.*)?\/control$/i.test(path) && !/\/control\/control$/i.test(path),
name: 'control'
})
export class FormControlRenderer extends FormControl {
static displayName = 'Control';
static contextType = ScopedContext;
controlRef(ref:any) {
controlRef(ref: any) {
const originRef = this.control;
super.controlRef(ref);
const scoped = this.context as IScopedContext;
@ -516,4 +472,3 @@ export class FormControlRenderer extends FormControl {
}
}
}

View File

@ -1,12 +1,7 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import {
filterDate
} from '../../utils/tpl-builtin';
import {filterDate} from '../../utils/tpl-builtin';
import moment from 'moment';
import 'moment/locale/zh-cn';
import DatePicker from '../../components/DatePicker';
@ -20,7 +15,7 @@ export interface DateProps extends FormControlProps {
closeOnSelect?: boolean;
disabled?: boolean;
iconClassName?: string;
};
}
interface DateControlState {
minDate?: moment.Moment;
@ -42,15 +37,7 @@ export default class DateControl extends React.PureComponent<DateProps> {
};
componentWillMount() {
const {
minDate,
maxDate,
value,
defaultValue,
setPrinstineValue,
data,
format
} = this.props;
const {minDate, maxDate, value, defaultValue, setPrinstineValue, data, format} = this.props;
if (defaultValue && value === defaultValue) {
setPrinstineValue(filterDate(defaultValue, data).format(format));
@ -69,24 +56,26 @@ export default class DateControl extends React.PureComponent<DateProps> {
nextProps.setPrinstineValue(filterDate(nextProps.defaultValue, nextProps.data));
}
if (props.minDate !== nextProps.minDate || props.maxDate !== nextProps.maxDate || props.data !== nextProps.data) {
if (
props.minDate !== nextProps.minDate ||
props.maxDate !== nextProps.maxDate ||
props.data !== nextProps.data
) {
this.setState({
minDate: nextProps.minDate ? filterDate(nextProps.minDate, nextProps.data, this.props.format) : undefined,
maxDate: nextProps.maxDate ? filterDate(nextProps.maxDate, nextProps.data, this.props.format) : undefined
minDate: nextProps.minDate
? filterDate(nextProps.minDate, nextProps.data, this.props.format)
: undefined,
maxDate: nextProps.maxDate
? filterDate(nextProps.maxDate, nextProps.data, this.props.format)
: undefined
});
}
}
render() {
const {
className,
classPrefix: ns,
defaultValue,
defaultData,
...rest
} = this.props;
const {className, classPrefix: ns, defaultValue, defaultData, ...rest} = this.props;
return (
return (
<div className={cx(`${ns}DateControl`, className)}>
<DatePicker {...rest} {...this.state} classPrefix={ns} />
</div>
@ -106,7 +95,7 @@ export class DateControlRenderer extends DateControl {
timeFormat: '',
strictMode: false
};
};
}
@FormItem({
type: 'datetime'
@ -121,7 +110,7 @@ export class DatetimeControlRenderer extends DateControl {
closeOnSelect: false,
strictMode: false
};
};
}
@FormItem({
type: 'time'
@ -136,4 +125,4 @@ export class TimeControlRenderer extends DateControl {
viewMode: 'time',
closeOnSelect: false
};
};
}

View File

@ -1,12 +1,7 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import {
filterDate
} from '../../utils/tpl-builtin';
import {filterDate} from '../../utils/tpl-builtin';
import moment from 'moment';
import 'moment/locale/zh-cn';
import DateRangePicker from '../../components/DateRangePicker';
@ -19,7 +14,7 @@ export interface DateRangeProps extends FormControlProps {
delimiter: string;
minDate?: any;
maxDate?: any;
};
}
interface DateControlState {
minDate?: moment.Moment;
@ -27,21 +22,16 @@ interface DateControlState {
}
export default class DateRangeControl extends React.Component<DateRangeProps, DateControlState> {
static defaultProps = {
format: 'X',
joinValues: true,
delimiter: ','
};
constructor(props:DateRangeProps) {
constructor(props: DateRangeProps) {
super(props);
const {
minDate,
maxDate,
data
} = props;
const {minDate, maxDate, data} = props;
this.state = {
minDate: minDate ? filterDate(minDate, data) : undefined,
@ -50,31 +40,26 @@ export default class DateRangeControl extends React.Component<DateRangeProps, Da
}
componentWillMount() {
const {
defaultValue,
setPrinstineValue,
delimiter,
format,
data,
value,
joinValues,
} = this.props;
const {defaultValue, setPrinstineValue, delimiter, format, data, value, joinValues} = this.props;
if (defaultValue && value === defaultValue) {
let arr = typeof defaultValue === 'string' ? defaultValue.split(delimiter) : defaultValue;
setPrinstineValue(DateRangePicker.formatValue({
startDate: filterDate(arr[0], data),
endDate: filterDate(arr[1], data),
}, format, joinValues, delimiter));
setPrinstineValue(
DateRangePicker.formatValue(
{
startDate: filterDate(arr[0], data),
endDate: filterDate(arr[1], data)
},
format,
joinValues,
delimiter
)
);
}
}
componentWillReceiveProps(nextProps: DateRangeProps) {
const {
data,
minDate,
maxDate
} = nextProps;
const {data, minDate, maxDate} = nextProps;
const props = this.props;
if (props.minDate !== minDate || props.maxDate !== maxDate || props.data !== data) {
@ -86,37 +71,31 @@ export default class DateRangeControl extends React.Component<DateRangeProps, Da
}
componentDidUpdate(prevProps: DateRangeProps) {
const {
defaultValue,
delimiter,
format,
joinValues,
setPrinstineValue,
data
} = this.props;
const {defaultValue, delimiter, format, joinValues, setPrinstineValue, data} = this.props;
if (prevProps.defaultValue !== defaultValue) {
let arr = typeof defaultValue === 'string'
? defaultValue.split(delimiter) : defaultValue;
setPrinstineValue(arr ? DateRangePicker.formatValue({
startDate: filterDate(arr[0], data),
endDate: filterDate(arr[1], data),
}, format, joinValues, delimiter) : undefined);
let arr = typeof defaultValue === 'string' ? defaultValue.split(delimiter) : defaultValue;
setPrinstineValue(
arr
? DateRangePicker.formatValue(
{
startDate: filterDate(arr[0], data),
endDate: filterDate(arr[1], data)
},
format,
joinValues,
delimiter
)
: undefined
);
}
}
render() {
const {
className,
classPrefix: ns,
defaultValue,
defaultData,
...rest
} = this.props;
const {className, classPrefix: ns, defaultValue, defaultData, ...rest} = this.props;
return (
return (
<div className={cx(`${ns}DateRangeControl`, className)}>
<DateRangePicker {...rest} {...this.state} classPrefix={ns} />
</div>
@ -132,7 +111,7 @@ export class DateRangeControlRenderer extends DateRangeControl {
...DateRangeControl.defaultProps,
timeFormat: ''
};
};
}
@FormItem({
type: 'datetime-range',
@ -144,5 +123,4 @@ export class DateTimeRangeControlRenderer extends DateRangeControl {
timeFormat: 'HH:mm',
inputFormat: 'YYYY-MM-DD HH:mm'
};
};
}

View File

@ -1,20 +1,15 @@
import React from 'react';
import {
Renderer
} from '../../factory';
import {
FormItem,
FormControlProps
} from './Item';
import {
filter,
} from '../../utils/tpl';
import {Renderer} from '../../factory';
import {FormItem, FormControlProps} from './Item';
import {filter} from '../../utils/tpl';
import cx from 'classnames';
import LazyComponent from '../../components/LazyComponent';
import debouce = require('lodash/debounce');
function loadComponent(): Promise<React.ReactType> {
return new Promise((resolve) => (require as any)(['../../components/Editor'], (component: any) => resolve(component.default)));
return new Promise(resolve =>
(require as any)(['../../components/Editor'], (component: any) => resolve(component.default))
);
}
export interface DiffEditorProps extends FormControlProps {
@ -30,7 +25,7 @@ function normalizeValue(value: any) {
return value;
}
export class DiffEditor extends React.Component<DiffEditorProps, any>{
export class DiffEditor extends React.Component<DiffEditorProps, any> {
static defaultProps: Partial<DiffEditorProps> = {
language: 'javascript',
theme: 'vs',
@ -44,10 +39,10 @@ export class DiffEditor extends React.Component<DiffEditorProps, any>{
}
},
diffValue: ''
}
};
state = {
focused: false,
focused: false
};
editor: any;
@ -88,15 +83,16 @@ export class DiffEditor extends React.Component<DiffEditorProps, any>{
}
componentDidUpdate(prevProps: any) {
const {
data,
value,
diffValue
} = this.props;
const {data, value, diffValue} = this.props;
if (this.originalEditor && diffValue && (diffValue !== prevProps.diffValue || data !== prevProps.data)) {
this.originalEditor.getModel().setValue(/^\$(?:([a-z0-9_.]+)|{.+})$/.test(diffValue as string)
? filter(normalizeValue(diffValue || ''), data, '| raw') : normalizeValue(diffValue));
this.originalEditor
.getModel()
.setValue(
/^\$(?:([a-z0-9_.]+)|{.+})$/.test(diffValue as string)
? filter(normalizeValue(diffValue || ''), data, '| raw')
: normalizeValue(diffValue)
);
}
if (this.modifiedEditor && value && value !== prevProps.value && !this.state.focused) {
@ -109,12 +105,7 @@ export class DiffEditor extends React.Component<DiffEditorProps, any>{
}
handleEditorMounted(editor: any, monaco: any) {
const {
value,
data,
language,
diffValue
} = this.props;
const {value, data, language, diffValue} = this.props;
this.monaco = monaco;
this.editor = editor;
@ -126,8 +117,12 @@ export class DiffEditor extends React.Component<DiffEditorProps, any>{
this.toDispose.push(this.modifiedEditor.onDidChangeModelContent(this.handleModifiedEditorChange).dispose);
this.editor.setModel({
original: this.monaco.editor.createModel(/^\$(?:([a-z0-9_.]+)|{.+})$/.test(diffValue as string)
? filter(normalizeValue(diffValue || ''), data, '| raw') : normalizeValue(diffValue), language),
original: this.monaco.editor.createModel(
/^\$(?:([a-z0-9_.]+)|{.+})$/.test(diffValue as string)
? filter(normalizeValue(diffValue || ''), data, '| raw')
: normalizeValue(diffValue),
language
),
modified: this.monaco.editor.createModel(normalizeValue(value), language)
});
@ -135,7 +130,7 @@ export class DiffEditor extends React.Component<DiffEditorProps, any>{
}
handleModifiedEditorChange() {
const { onChange } = this.props;
const {onChange} = this.props;
onChange && onChange(this.modifiedEditor.getModel().getValue());
this.updateContainerSize();
}
@ -153,21 +148,13 @@ export class DiffEditor extends React.Component<DiffEditorProps, any>{
}
render() {
const {
className,
value,
onChange,
disabled,
size,
options,
language,
theme,
classnames: cx
} = this.props;
const {className, value, onChange, disabled, size, options, language, theme, classnames: cx} = this.props;
return (
<div
className={cx('EditorControl', size ? `EditorControl--${size}` : '', className, { 'is-focused': this.state.focused })}
className={cx('EditorControl', size ? `EditorControl--${size}` : '', className, {
'is-focused': this.state.focused
})}
>
<LazyComponent
getComponent={loadComponent}
@ -194,14 +181,14 @@ export class DiffEditor extends React.Component<DiffEditorProps, any>{
})
export class DiffEditorControlRenderer extends DiffEditor {
static defaultProps = {
...DiffEditor.defaultProps,
...DiffEditor.defaultProps
};
};
}
@Renderer({
test: /(^|\/)diff-editor$/,
sizeMutable: false,
name: "diff-editor"
name: 'diff-editor'
})
export class DiffEditorRenderer extends DiffEditor {
static defaultProps = {

View File

@ -1,8 +1,5 @@
import React from 'react';
import {
Renderer,
RendererProps
} from '../../factory';
import {Renderer, RendererProps} from '../../factory';
import Collapse from '../Collapse';
import cx from 'classnames';
@ -10,19 +7,19 @@ export interface FieldSetProps extends RendererProps {
title?: string;
collapsed?: boolean;
mode?: 'normal' | 'inline' | 'horizontal' | 'row';
size?: 'xs' | 'sm' | 'md' | 'lg' | 'base',
size?: 'xs' | 'sm' | 'md' | 'lg' | 'base';
collapsable?: boolean;
horizontal: {
left: string;
right: string;
offset: string;
};
};
}
export default class FieldSetControl extends React.Component<FieldSetProps, any> {
constructor(props:FieldSetProps) {
constructor(props: FieldSetProps) {
super(props);
this.renderBody = this.renderBody.bind(this);
this.renderBody = this.renderBody.bind(this);
}
static defaultProps = {
@ -30,7 +27,7 @@ export default class FieldSetControl extends React.Component<FieldSetProps, any>
collapsable: false
};
renderBody():JSX.Element {
renderBody(): JSX.Element {
const {
renderFormItems,
controls,
@ -49,7 +46,7 @@ export default class FieldSetControl extends React.Component<FieldSetProps, any>
return render('body', body) as JSX.Element;
}
let props:any = {
let props: any = {
store,
data: store.data,
render
@ -66,15 +63,10 @@ export default class FieldSetControl extends React.Component<FieldSetProps, any>
}
render() {
const {
controls,
className,
mode,
...rest
} = this.props;
const {controls, className, mode, ...rest} = this.props;
return (
<Collapse
<Collapse
{...rest}
className={className}
children={this.renderBody}
@ -88,6 +80,6 @@ export default class FieldSetControl extends React.Component<FieldSetProps, any>
@Renderer({
test: /(^|\/)form(?:.+)?\/control\/fieldSet$/i,
weight: -100,
name: "fieldset"
name: 'fieldset'
})
export class FieldSetRenderer extends FieldSetControl {};
export class FieldSetRenderer extends FieldSetControl {}

View File

@ -1,8 +1,5 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import qs from 'qs';
import find = require('lodash/find');
@ -10,7 +7,7 @@ import isPlainObject = require('lodash/isPlainObject');
import {mapLimit} from 'async';
import ImageControl from './Image';
import {Payload} from '../../types';
import { filter } from '../../utils/tpl';
import {filter} from '../../utils/tpl';
import Alert from '../../components/Alert2';
export interface FileProps extends FormControlProps {
@ -41,12 +38,12 @@ export interface FileProps extends FormControlProps {
uploading: string;
error: string;
uploaded: string;
[propName:string]: string;
[propName: string]: string;
};
asBase64?: boolean;
asBlob?: boolean;
resetValue?: string;
};
}
export interface FileX extends File {
state?: 'init' | 'error' | 'pending' | 'uploading' | 'uploaded' | 'invalid' | 'ready';
@ -58,17 +55,17 @@ export interface FileValue {
name?: string;
url?: string;
state: 'init' | 'error' | 'pending' | 'uploading' | 'uploaded' | 'invalid' | 'ready';
[propName:string]: any;
};
[propName: string]: any;
}
export interface FileState {
uploading: boolean;
files: Array<FileX | FileValue>;
error?: string | null;
};
}
export default class FileControl extends React.Component<FileProps, FileState> {
static defaultProps:Partial<FileProps> = {
static defaultProps: Partial<FileProps> = {
btnClassName: 'btn-sm btn-info',
btnUploadClassName: 'btn-sm btn-success',
maxSize: 0,
@ -91,52 +88,69 @@ export default class FileControl extends React.Component<FileProps, FileState> {
autoUpload: true,
hideUploadButton: false,
stateTextMap: {
'init': "",
'pending': "等待上传",
'uploading': "上传中",
'error': "上传出错",
'uploaded': "已上传",
"ready": ""
init: '',
pending: '等待上传',
uploading: '上传中',
error: '上传出错',
uploaded: '已上传',
ready: ''
},
asBase64: false
};
state: FileState;
current: FileValue | FileX | null;
resolve?: (value?:any) => void;
resolve?: (value?: any) => void;
static valueToFile(value:string | FileValue, props:FileProps, files?: Array<FileX | FileValue>):FileValue|undefined {
let file:FileValue | FileX | undefined = files && typeof value === 'string'
? find(files, item => (item as FileValue).value === value)
static valueToFile(
value: string | FileValue,
props: FileProps,
files?: Array<FileX | FileValue>
): FileValue | undefined {
let file: FileValue | FileX | undefined =
files && typeof value === 'string' ? find(files, item => (item as FileValue).value === value) : undefined;
return value
? value instanceof File
? {
state: 'ready',
value: value,
name: value.name,
url: ''
}
: {
...(typeof value === 'string'
? {
state: file && file.state ? file.state : 'init',
value,
name: /^data:/.test(value) ? (file && file.name) || 'base64数据' : '',
url:
typeof props.downloadUrl === 'string' && value && !/^data:/.test(value)
? `${props.downloadUrl}${value}`
: undefined
}
: (value as FileValue))
}
: undefined;
return value ? value instanceof File ? {
state: 'ready',
value: value,
name: value.name,
url: ''
} : {
...(typeof value === 'string' ? {
state: file && file.state ? file.state : 'init',
value,
name: /^data:/.test(value) ? (file && file.name || 'base64数据') : '',
url: typeof props.downloadUrl === 'string' && value && !/^data:/.test(value) ? `${props.downloadUrl}${value}` : undefined
} : (value as FileValue))
} : undefined;
}
constructor(props:FileProps) {
constructor(props: FileProps) {
super(props);
const value:string|Array<string | FileValue>|FileValue = props.value;
const value: string | Array<string | FileValue> | FileValue = props.value;
const multiple = props.multiple;
const joinValues = props.joinValues;
const delimiter = props.delimiter as string;
let files:Array<FileValue> = [];
let files: Array<FileValue> = [];
if (value) {
files = (Array.isArray(value) ? value : joinValues ? (((value as any).value || value) as string).split(delimiter) : [((value as any).value || value) as string])
.map(item => FileControl.valueToFile(item, props) as FileValue)
.filter(item => item);
files = (Array.isArray(value)
? value
: joinValues
? (((value as any).value || value) as string).split(delimiter)
: [((value as any).value || value) as string]
)
.map(item => FileControl.valueToFile(item, props) as FileValue)
.filter(item => item);
}
this.state = {
@ -157,42 +171,49 @@ export default class FileControl extends React.Component<FileProps, FileState> {
this.uploadBigFile = this.uploadBigFile.bind(this);
}
componentWillReceiveProps(nextProps:FileProps) {
componentWillReceiveProps(nextProps: FileProps) {
const props = this.props;
if (props.value !== nextProps.value) {
const value:string|Array<string | FileValue>|FileValue = nextProps.value;
const value: string | Array<string | FileValue> | FileValue = nextProps.value;
const multiple = nextProps.multiple;
const joinValues =nextProps.joinValues;
const joinValues = nextProps.joinValues;
const delimiter = nextProps.delimiter as string;
let files:Array<FileValue> = [];
let files: Array<FileValue> = [];
if (value) {
files = (Array.isArray(value) ? value : joinValues && typeof value === 'string' ? value.split(delimiter) : [value as any])
.map(item => {
let obj = FileControl.valueToFile(item, nextProps, this.state.files) as FileValue;
let org;
files = (Array.isArray(value)
? value
: joinValues && typeof value === 'string'
? value.split(delimiter)
: [value as any]
)
.map(item => {
let obj = FileControl.valueToFile(item, nextProps, this.state.files) as FileValue;
let org;
if (obj && (org = find(this.state.files, (item:FileValue) => item.value === obj.value)) as FileValue) {
obj = {
...org,
...obj
};
}
if (
obj &&
((org = find(this.state.files, (item: FileValue) => item.value === obj.value)) as FileValue)
) {
obj = {
...org,
...obj
};
}
return obj;
})
.filter(item => item);
return obj;
})
.filter(item => item);
}
this.setState({
files: files,
files: files
});
}
}
handleDrop(e:React.ChangeEvent<any>) {
handleDrop(e: React.ChangeEvent<any>) {
const files = e.currentTarget.files;
if (!files.length) {
@ -200,13 +221,18 @@ export default class FileControl extends React.Component<FileProps, FileState> {
}
const {maxSize, multiple, maxLength} = this.props;
const allowed = (multiple ? maxLength ? maxLength : (files.length + this.state.files.length) : 1) - this.state.files.length;
const allowed =
(multiple ? (maxLength ? maxLength : files.length + this.state.files.length) : 1) - this.state.files.length;
const inputFiles:Array<FileX> = [];
const inputFiles: Array<FileX> = [];
[].slice.call(files, 0, allowed).forEach((file:FileX) => {
[].slice.call(files, 0, allowed).forEach((file: FileX) => {
if (maxSize && file.size > maxSize) {
alert(`您选择的文件 ${file.name} 大小为 ${ImageControl.formatFileSize(file.size)} 超出了最大为 ${ImageControl.formatFileSize(maxSize)} 的限制,请重新选择`);
alert(
`您选择的文件 ${file.name} 大小为 ${ImageControl.formatFileSize(
file.size
)} ${ImageControl.formatFileSize(maxSize)} `
);
return;
}
@ -218,18 +244,19 @@ export default class FileControl extends React.Component<FileProps, FileState> {
return;
}
this.setState({
error: null,
files: this.state.files.concat(inputFiles)
}, () => {
const {
autoUpload
} = this.props;
this.setState(
{
error: null,
files: this.state.files.concat(inputFiles)
},
() => {
const {autoUpload} = this.props;
if (autoUpload) {
this.startUpload();
if (autoUpload) {
this.startUpload();
}
}
});
);
}
startUpload() {
@ -237,19 +264,22 @@ export default class FileControl extends React.Component<FileProps, FileState> {
return;
}
this.setState({
uploading: true,
files: this.state.files.map(file => {
if (file.state === 'error') {
file.state = 'pending';
}
this.setState(
{
uploading: true,
files: this.state.files.map(file => {
if (file.state === 'error') {
file.state = 'pending';
}
return file;
})
}, this.tick);
return file;
})
},
this.tick
);
}
toggleUpload(e:React.MouseEvent<HTMLButtonElement>) {
toggleUpload(e: React.MouseEvent<HTMLButtonElement>) {
e.preventDefault();
return this.state.uploading ? this.stopUpload() : this.startUpload();
}
@ -265,7 +295,9 @@ export default class FileControl extends React.Component<FileProps, FileState> {
}
tick() {
if (this.current || !this.state.uploading) {return;}
if (this.current || !this.state.uploading) {
return;
}
const file = find(this.state.files, item => item.state === 'pending') as FileX;
if (file) {
@ -273,47 +305,58 @@ export default class FileControl extends React.Component<FileProps, FileState> {
file.state = 'uploading';
this.setState({
files: this.state.files.concat()
}, () => this.sendFile(file, (error, file, obj) => {
const files = this.state.files.concat();
const idx = files.indexOf(file as FileX);
this.setState(
{
files: this.state.files.concat()
},
() =>
this.sendFile(file, (error, file, obj) => {
const files = this.state.files.concat();
const idx = files.indexOf(file as FileX);
if (!~idx) {
return;
}
if (!~idx) {
return;
}
let newFile:FileValue = file as FileValue;
let newFile: FileValue = file as FileValue;
if (error) {
newFile.state = 'error';
newFile.error = error;
} else {
newFile = obj as FileValue;
}
files.splice(idx, 1, newFile);
this.current = null;
this.setState({
error: error ? error : null,
files: files
}, this.tick);
}));
if (error) {
newFile.state = 'error';
newFile.error = error;
} else {
newFile = obj as FileValue;
}
files.splice(idx, 1, newFile);
this.current = null;
this.setState(
{
error: error ? error : null,
files: files
},
this.tick
);
})
);
} else {
this.setState({
uploading: false
}, () => {
this.onChange();
this.setState(
{
uploading: false
},
() => {
this.onChange();
if (this.resolve) {
this.resolve(this.state.files.some(file => file.state === 'error') ? '文件上传失败请重试' : null);
this.resolve = undefined;
if (this.resolve) {
this.resolve(
this.state.files.some(file => file.state === 'error') ? '文件上传失败请重试' : null
);
this.resolve = undefined;
}
}
});
);
}
}
sendFile(file:FileX, cb:(error:null|string, file?:FileX, obj?: FileValue) => void) {
sendFile(file: FileX, cb: (error: null | string, file?: FileX, obj?: FileValue) => void) {
const {
reciever,
fileField,
@ -337,55 +380,75 @@ export default class FileControl extends React.Component<FileProps, FileState> {
url: '',
state: 'ready'
});
}
reader.onerror = (error:any) => cb(error.message);
};
reader.onerror = (error: any) => cb(error.message);
return;
} else if (asBlob) {
setTimeout(() => cb(null, file, {
name: file.name,
value: file,
url: '',
state: 'ready'
}), 4);
setTimeout(
() =>
cb(null, file, {
name: file.name,
value: file,
url: '',
state: 'ready'
}),
4
);
return;
}
let fn = useChunk === 'auto' && chunkSize && file.size > chunkSize || useChunk === true ? this.uploadBigFile : this.uploadFile;
let fn =
(useChunk === 'auto' && chunkSize && file.size > chunkSize) || useChunk === true
? this.uploadBigFile
: this.uploadFile;
fn(file, reciever as string, {}, {
fieldName: fileField,
chunkSize,
startChunkApi,
chunkApi,
finishChunkApi
})
.then(ret => {
if (ret.status || !ret.data) {
throw new Error(ret.msg || '上传失败, 请重试');
fn(
file,
reciever as string,
{},
{
fieldName: fileField,
chunkSize,
startChunkApi,
chunkApi,
finishChunkApi
}
)
.then(ret => {
if (ret.status || !ret.data) {
throw new Error(ret.msg || '上传失败, 请重试');
}
const value = (ret.data as any).value || ret.data;
const value = (ret.data as any).value || ret.data;
cb(null, file, {
...isPlainObject(ret.data) ? ret.data : null,
value: value,
url: typeof downloadUrl === 'string' && value ? `${downloadUrl}${value}` : ret.data ? (ret.data as any).url : null,
state: 'uploaded'
cb(null, file, {
...(isPlainObject(ret.data) ? ret.data : null),
value: value,
url:
typeof downloadUrl === 'string' && value
? `${downloadUrl}${value}`
: ret.data
? (ret.data as any).url
: null,
state: 'uploaded'
});
})
.catch(error => {
cb(error.message || '上传失败, 请重试', file);
});
})
.catch(error => {
cb(error.message || '上传失败, 请重试', file);
})
}
removeFile(file:FileX | FileValue, index:number) {
removeFile(file: FileX | FileValue, index: number) {
const files = this.state.files.concat();
files.splice(index, 1);
this.setState({
files: files
}, this.onChange);
this.setState(
{
files: files
},
this.onChange
);
}
clearError() {
@ -395,26 +458,21 @@ export default class FileControl extends React.Component<FileProps, FileState> {
}
onChange() {
const {
multiple,
onChange,
joinValues,
extractValue,
valueField,
delimiter,
resetValue,
asBlob
} = this.props;
const {multiple, onChange, joinValues, extractValue, valueField, delimiter, resetValue, asBlob} = this.props;
const files = this.state.files.filter(file => ~['uploaded', 'init', 'ready'].indexOf(file.state as string));
let value:any = multiple ? files : files[0];
let value: any = multiple ? files : files[0];
if (value) {
if (extractValue || asBlob) {
value = Array.isArray(value) ? value.map((item: any) => item[valueField || 'value']) : value[valueField || 'value'];
value = Array.isArray(value)
? value.map((item: any) => item[valueField || 'value'])
: value[valueField || 'value'];
} else if (joinValues) {
value = Array.isArray(value) ? value.map((item: any) => item[valueField || 'value']).join(delimiter || ',') : value[valueField || 'value'];
}
value = Array.isArray(value)
? value.map((item: any) => item[valueField || 'value']).join(delimiter || ',')
: value[valueField || 'value'];
}
} else {
value = typeof resetValue === 'undefined' ? '' : resetValue;
}
@ -422,7 +480,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
onChange(value);
}
uploadFile(file:FileX, reciever:string, params:object, config:Partial<FileProps> = {}):Promise<Payload> {
uploadFile(file: FileX, reciever: string, params: object, config: Partial<FileProps> = {}): Promise<Payload> {
const fd = new FormData();
reciever = filter(reciever, this.props.data);
@ -445,7 +503,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
});
}
uploadBigFile(file:FileX, reciever:string, params:object, config:Partial<FileProps> = {}):Promise<Payload> {
uploadBigFile(file: FileX, reciever: string, params: object, config: Partial<FileProps> = {}): Promise<Payload> {
const chunkSize = config.chunkSize || 5 * 1024 * 1024;
const self = this;
@ -454,8 +512,8 @@ export default class FileControl extends React.Component<FileProps, FileState> {
uploadId: string;
loaded: number;
total: number;
[propName:string]: any;
};
[propName: string]: any;
}
interface Task {
file: File;
@ -463,18 +521,17 @@ export default class FileControl extends React.Component<FileProps, FileState> {
partSize: number;
start: number;
stop: number;
[propName:string]: any;
};
[propName: string]: any;
}
return new Promise((resolve, reject) => {
let state:ObjectState;
let state: ObjectState;
self._send(config.startChunkApi as string, {filename: file.name})
.then(startChunk)
.catch(reject);
.then(startChunk)
.catch(reject);
function startChunk(ret:Payload) {
function startChunk(ret: Payload) {
const tasks = getTasks(file);
if (!ret.data) {
@ -488,7 +545,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
total: tasks.length
};
mapLimit(tasks, 3, uploadPartFile(state, config), function (err, results) {
mapLimit(tasks, 3, uploadPartFile(state, config), function(err, results) {
if (err) {
reject(err);
} else {
@ -497,20 +554,22 @@ export default class FileControl extends React.Component<FileProps, FileState> {
});
}
function finishChunk(partList:Array<any>|undefined, state:ObjectState) {
function finishChunk(partList: Array<any> | undefined, state: ObjectState) {
self._send(config.finishChunkApi as string, {
...params,
uploadId: state.uploadId,
key: state.key,
filename: file.name,
partList
}).then(resolve).catch(reject);
})
.then(resolve)
.catch(reject);
}
function uploadPartFile(state:ObjectState, conf:Partial<FileProps>) {
function uploadPartFile(state: ObjectState, conf: Partial<FileProps>) {
reciever = conf.chunkApi as string;
return (task:Task, callback:(error:any, value?:any) => void) => {
return (task: Task, callback: (error: any, value?: any) => void) => {
const fd = new FormData();
let blob = task.file.slice(task.start, task.stop + 1);
@ -520,26 +579,27 @@ export default class FileControl extends React.Component<FileProps, FileState> {
fd.append('partSize', task.partSize.toString());
fd.append(config.fieldName || 'file', blob, file.name);
return self._send(reciever, fd, {
withCredentials: true
})
.then(ret => {
state.loaded++;
callback(null, {
partNumber: task.partNumber,
eTag: (ret.data as any).eTag
});
})
.catch(callback);
}
return self
._send(reciever, fd, {
withCredentials: true
})
.then(ret => {
state.loaded++;
callback(null, {
partNumber: task.partNumber,
eTag: (ret.data as any).eTag
});
})
.catch(callback);
};
}
function getTasks(file:FileX):Array<Task> {
function getTasks(file: FileX): Array<Task> {
let leftSize = file.size;
let offset = 0;
let partNumber = 1;
let tasks:Array<Task> = [];
let tasks: Array<Task> = [];
while (leftSize > 0) {
let partSize = Math.min(leftSize, chunkSize);
@ -558,10 +618,10 @@ export default class FileControl extends React.Component<FileProps, FileState> {
return tasks;
}
})
});
}
_send(reciever:string, data:any, options?:object):Promise<Payload> {
_send(reciever: string, data: any, options?: object): Promise<Payload> {
const env = this.props.env;
if (!env || !env.fetcher) {
@ -575,9 +635,9 @@ export default class FileControl extends React.Component<FileProps, FileState> {
});
}
validate():any {
validate(): any {
if (this.state.uploading || this.state.files.some(item => item.state === 'pending')) {
return new Promise((resolve) => {
return new Promise(resolve => {
this.resolve = resolve;
this.startUpload();
});
@ -602,11 +662,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
asBlob,
joinValues
} = this.props;
let {
files,
uploading,
error
} = this.state;
let {files, uploading, error} = this.state;
const hasPending = files.some(file => file.state == 'pending');
@ -620,47 +676,67 @@ export default class FileControl extends React.Component<FileProps, FileState> {
{files && files.length ? (
<ul className="list-group no-bg m-b-sm">
{files.map((file, key) => (
<li key={key} className="list-group-item clearfix">
<a
className="text-danger pull-right"
onClick={() => this.removeFile(file, key)}
href="javascript:void 0"
data-tooltip="移除"
><i className="fa fa-times" /></a>
<span className="pull-right text-muted text-xs m-r-sm">{stateTextMap && stateTextMap[file.state as string] || ''}</span>
<i className="fa fa-file fa-fw m-r-xs" />
{(file as FileValue).url ? (<a href={(file as FileValue).url} target="_blank">{file.name || (file as FileValue).filename || (file as FileValue).value}</a>) : (<span>{file.name || (file as FileValue).filename}</span>)}
</li>
))}
{files.map((file, key) => (
<li key={key} className="list-group-item clearfix">
<a
className="text-danger pull-right"
onClick={() => this.removeFile(file, key)}
href="javascript:void 0"
data-tooltip="移除"
>
<i className="fa fa-times" />
</a>
<span className="pull-right text-muted text-xs m-r-sm">
{(stateTextMap && stateTextMap[file.state as string]) || ''}
</span>
<i className="fa fa-file fa-fw m-r-xs" />
{(file as FileValue).url ? (
<a href={(file as FileValue).url} target="_blank">
{file.name || (file as FileValue).filename || (file as FileValue).value}
</a>
) : (
<span>{file.name || (file as FileValue).filename}</span>
)}
</li>
))}
</ul>
) : null}
<div className="clear">
{multiple && (!maxLength || files.length < maxLength) || !multiple && !files.length ? (
<label className={cx("btn m-r-xs", btnClassName, {disabled})}>
<input type="file" accept={accept} disabled={disabled} multiple={multiple} className="invisible" onChange={this.handleDrop} />
{btnLabel}
</label>
) : null}
{(multiple && (!maxLength || files.length < maxLength)) || (!multiple && !files.length) ? (
<label className={cx('btn m-r-xs', btnClassName, {disabled})}>
<input
type="file"
accept={accept}
disabled={disabled}
multiple={multiple}
className="invisible"
onChange={this.handleDrop}
/>
{btnLabel}
</label>
) : null}
{!autoUpload && !hideUploadButton && files.length ? (
<button type="button" className={cx('btn m-r-xs', btnUploadClassName)} disabled={!hasPending} onClick={this.toggleUpload}>
{uploading ? '暂停上传' : '开始上传'}
</button>
) : null}
{!autoUpload && !hideUploadButton && files.length ? (
<button
type="button"
className={cx('btn m-r-xs', btnUploadClassName)}
disabled={!hasPending}
onClick={this.toggleUpload}
>
{uploading ? '暂停上传' : '开始上传'}
</button>
) : null}
{this.state.uploading ? (<i className="fa fa-spinner fa-spin fa-2x fa-fw" />) : null}
{this.state.uploading ? <i className="fa fa-spinner fa-spin fa-2x fa-fw" /> : null}
</div>
</div>
);
}
}
@FormItem({
type: 'file',
sizeMutable: false
})
export class FileControlRenderer extends FileControl {};
export class FileControlRenderer extends FileControl {}

View File

@ -1,15 +1,7 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {
evalJS,
filter
} from '../../utils/tpl';
import {
isObjectShallowModified
} from '../../utils/helper';
import {FormItem, FormControlProps} from './Item';
import {evalJS, filter} from '../../utils/tpl';
import {isObjectShallowModified} from '../../utils/helper';
export interface FormulaProps extends FormControlProps {
formula?: string;
@ -19,14 +11,8 @@ export interface FormulaProps extends FormControlProps {
}
export default class FormulaControl extends React.Component<FormControlProps, any> {
componentDidMount() {
const {
formula,
data,
setPrinstineValue,
initSet
} = this.props;
const {formula, data, setPrinstineValue, initSet} = this.props;
if (!formula || initSet === false) {
return;
@ -37,24 +23,25 @@ export default class FormulaControl extends React.Component<FormControlProps, an
}
componentWillReceiveProps(nextProps: FormControlProps) {
const {
formula,
data,
onChange,
autoSet,
value,
condition
} = this.props;
if (autoSet !== false && formula && nextProps.formula && isObjectShallowModified(data, nextProps.data, false) && value === nextProps.value) {
const {formula, data, onChange, autoSet, value, condition} = this.props;
if (
autoSet !== false &&
formula &&
nextProps.formula &&
isObjectShallowModified(data, nextProps.data, false) &&
value === nextProps.value
) {
const nextResult: any = evalJS(nextProps.formula, nextProps.data as object);
if (condition && nextProps.condition) {
if (!!~condition.indexOf("$") || !!~condition.indexOf("<%")) { // 使用${xxx},来监听某个变量的变化
if (!!~condition.indexOf('$') || !!~condition.indexOf('<%')) {
// 使用${xxx},来监听某个变量的变化
if (filter(condition, data) !== filter(nextProps.condition, nextProps.data)) {
onChange(nextResult);
}
} else if (evalJS(nextProps.condition, nextProps.data as object)) { // 使用 data.xxx == 'a' 表达式形式来判断
} else if (evalJS(nextProps.condition, nextProps.data as object)) {
// 使用 data.xxx == 'a' 表达式形式来判断
onChange(nextResult);
}
} else {
@ -69,13 +56,7 @@ export default class FormulaControl extends React.Component<FormControlProps, an
doAction() {
// 不细化具体是啥动作了,先重新计算,并把值运用上。
const {
formula,
data,
onChange,
autoSet,
value
} = this.props;
const {formula, data, onChange, autoSet, value} = this.props;
const result: any = evalJS(formula, data as object);
onChange(result);
@ -92,4 +73,4 @@ export default class FormulaControl extends React.Component<FormControlProps, an
strictMode: false,
sizeMutable: false
})
export class FormulaControlRenderer extends FormulaControl { };
export class FormulaControlRenderer extends FormulaControl {}

View File

@ -1,19 +1,13 @@
import {
Renderer,
RendererProps
} from '../../factory';
import Grid, { ColumnNode, Column, ColProps, ColumnArray } from '../Grid';
import { Schema } from '../../types';
import {Renderer, RendererProps} from '../../factory';
import Grid, {ColumnNode, Column, ColProps, ColumnArray} from '../Grid';
import {Schema} from '../../types';
import {
FormItem,
FormControlProps
} from './Item';
import pick = require("lodash/pick");
import {FormItem, FormControlProps} from './Item';
import pick = require('lodash/pick');
import React from 'react';
import cx from 'classnames';
export interface GridProps extends FormControlProps {};
export interface GridProps extends FormControlProps {}
const defaultHorizontal = {
left: 'col-sm-4',
right: 'col-sm-8',
@ -26,18 +20,11 @@ const defaultHorizontal = {
sizeMutable: false
})
export class GridRenderer extends Grid<GridProps> {
static propsList: Array<string> = ["columns"];
static propsList: Array<string> = ['columns'];
static defaultProps = {};
renderChild(region:string, node:Schema, key: number, length: number) {
const {
render,
renderFormItems,
classnames: cx,
$path,
itemRender,
store
} = this.props;
renderChild(region: string, node: Schema, key: number, length: number) {
const {render, renderFormItems, classnames: cx, $path, itemRender, store} = this.props;
if (node && !node.type && (node.controls || node.tabs || node.feildSet)) {
return (
@ -53,6 +40,6 @@ export class GridRenderer extends Grid<GridProps> {
);
}
return itemRender ? itemRender(node, key, length, this.props) : render(region, node.body || node);
return itemRender ? itemRender(node, key, length, this.props) : render(region, node.body || node);
}
}

View File

@ -1,44 +1,42 @@
import React from 'react';
import {
Renderer,
RendererProps
} from '../../factory';
import { Schema } from '../../types';
import {Renderer, RendererProps} from '../../factory';
import {Schema} from '../../types';
import {isVisible, getWidthRate, makeHorizontalDeeper} from '../../utils/helper';
import cx from 'classnames';
import { FormItemWrap } from './Item';
import {FormItemWrap} from './Item';
export interface InputGroupProps extends RendererProps {
formMode?: string;
controls: Array<any>;
gap?: 'xs' | 'sm' | 'normal';
direction: 'horizontal' | 'vertical';
};
}
@Renderer({
test: /(^|\/)form(?:\/.+)?\/control\/(?:\d+\/)?group$/,
sizeMutable: false,
name: "group-control"
name: 'group-control'
})
export class ControlGroupRenderer extends React.Component<InputGroupProps> {
constructor(props:InputGroupProps) {
super(props)
constructor(props: InputGroupProps) {
super(props);
this.renderInput = this.renderInput.bind(this);
}
renderControl(control:any, index:any, otherProps?: any) {
const {
render
} = this.props;
renderControl(control: any, index: any, otherProps?: any) {
const {render} = this.props;
if (!control) {
return null;
}
const subSchema: any = control && (control as Schema).type === 'control' ? control : {
type: 'control',
control
};
const subSchema: any =
control && (control as Schema).type === 'control'
? control
: {
type: 'control',
control
};
if (subSchema.control) {
let control = subSchema.control as Schema;
@ -52,26 +50,19 @@ export class ControlGroupRenderer extends React.Component<InputGroupProps> {
}
renderVertical(props = this.props) {
let {
controls,
className,
classnames: cx,
mode,
formMode,
data,
} = props;
let {controls, className, classnames: cx, mode, formMode, data} = props;
formMode = mode || formMode;
controls = controls.filter(item => isVisible(item, data));
return (
<div className={cx(`Form-group Form-group--ver Form-group--${formMode}`, className)}>
{controls.map((control, index) => {
if (!isVisible(control, data)) {
return null;
}
const controlMode = control && control.mode || formMode;
const controlMode = (control && control.mode) || formMode;
return this.renderControl(control, index, {
key: index,
@ -79,7 +70,7 @@ export class ControlGroupRenderer extends React.Component<InputGroupProps> {
});
})}
</div>
)
);
}
renderHorizontal(props = this.props) {
@ -93,20 +84,26 @@ export class ControlGroupRenderer extends React.Component<InputGroupProps> {
formMode,
formHorizontal,
data,
gap,
gap
} = props;
formMode = mode || formMode;
return (
<div className={cx(`Form-group Form-group--hor Form-group--${formMode}`, gap ? `Form-group--${gap}` : '', className)}>
<div
className={cx(
`Form-group Form-group--hor Form-group--${formMode}`,
gap ? `Form-group--${gap}` : '',
className
)}
>
{controls.map((control, index) => {
if (!isVisible(control, data)) {
return null;
}
const controlMode = control && control.mode || formMode;
const controlMode = (control && control.mode) || formMode;
if (controlMode === 'inline' || control && control.type === "formula") {
if (controlMode === 'inline' || (control && control.type === 'formula')) {
return this.renderControl(control, index, {
formMode: 'inline',
key: index,
@ -115,10 +112,18 @@ export class ControlGroupRenderer extends React.Component<InputGroupProps> {
}
const columnWidth = control.columnRatio || getWidthRate(control && control.columnClassName);
let horizontalDeeper = horizontal || makeHorizontalDeeper(formHorizontal, controls.filter(item => item.mode !== 'inline').length);
let horizontalDeeper =
horizontal ||
makeHorizontalDeeper(formHorizontal, controls.filter(item => item.mode !== 'inline').length);
return (
<div key={index} className={cx(`${ns}Form-groupColumn`, columnWidth ? `${ns}Form-groupColumn--${columnWidth}` : '')}>
<div
key={index}
className={cx(
`${ns}Form-groupColumn`,
columnWidth ? `${ns}Form-groupColumn--${columnWidth}` : ''
)}
>
{this.renderControl(control, index, {
formHorizontal: horizontalDeeper,
formMode: controlMode
@ -127,29 +132,19 @@ export class ControlGroupRenderer extends React.Component<InputGroupProps> {
);
})}
</div>
)
);
}
renderInput(props = this.props) {
const direction = props.direction;
return direction === "vertical" ? this.renderVertical(props) : this.renderHorizontal(props);
return direction === 'vertical' ? this.renderVertical(props) : this.renderHorizontal(props);
}
render() {
const {
label,
...rest
} = this.props;
const {label, ...rest} = this.props;
if (label) {
return (
<FormItemWrap
{...rest as any}
sizeMutable={false}
label={label}
renderControl={this.renderInput}
/>
);
return <FormItemWrap {...rest as any} sizeMutable={false} label={label} renderControl={this.renderInput} />;
}
return this.renderInput();

View File

@ -1,20 +1,14 @@
import React from 'react';
import {
Renderer,
RendererProps
} from '../../factory';
import {
FormItem,
FormControlProps
} from './Item';
import {Renderer, RendererProps} from '../../factory';
import {FormItem, FormControlProps} from './Item';
import HBox from '../HBox';
import { Schema } from '../../types';
import {Schema} from '../../types';
import cx from 'classnames';
import { isVisible } from '../../utils/helper';
import {isVisible} from '../../utils/helper';
interface HBoxProps extends FormControlProps {
size?: string;
};
}
@FormItem({
type: 'hbox',
@ -22,15 +16,11 @@ interface HBoxProps extends FormControlProps {
sizeMutable: false
})
export class HBoxRenderer extends React.Component<HBoxProps, any> {
static propsList: Array<string> = ["columns"];
static propsList: Array<string> = ['columns'];
static defaultProps: Partial<HBoxProps> = {};
renderColumn(column: any, key: number, length: number) {
const {
itemRender,
data,
classPrefix: ns
} = this.props;
const {itemRender, data, classPrefix: ns} = this.props;
if (!isVisible(column, data)) {
return null;
@ -53,14 +43,8 @@ export class HBoxRenderer extends React.Component<HBoxProps, any> {
);
}
renderChild(region:string, node:Schema) {
const {
render,
renderFormItems,
formMode,
store,
$path
} = this.props;
renderChild(region: string, node: Schema) {
const {render, renderFormItems, formMode, store, $path} = this.props;
if (node && !node.type && (node.controls || node.tabs || node.feildSet)) {
return renderFormItems(node, ($path as string).replace(/^.*form\//, ''), {
@ -80,19 +64,12 @@ export class HBoxRenderer extends React.Component<HBoxProps, any> {
}
render() {
const {
className,
columns,
gap,
classPrefix: ns
} = this.props;
const {className, columns, gap, classPrefix: ns} = this.props;
return (
<div className={cx(`${ns}FormHbox`, gap ? `${ns}Hbox--${gap}` : '', className)}>
<div className={`${ns}Hbox`}>
{columns.map((column:any, key:number) =>
this.renderColumn(column, key, columns.length)
)}
{columns.map((column: any, key: number) => this.renderColumn(column, key, columns.length))}
</div>
</div>
);

View File

@ -1,8 +1,5 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
export default class HiddenControl extends React.Component<FormControlProps, any> {
render() {
@ -15,4 +12,4 @@ export default class HiddenControl extends React.Component<FormControlProps, any
wrap: false,
sizeMutable: false
})
export class HiddenControlRenderer extends HiddenControl {};
export class HiddenControlRenderer extends HiddenControl {}

View File

@ -1,50 +1,47 @@
import React from 'react';
import cx from 'classnames';
import matchSorter from 'match-sorter';
import keycode from "keycode";
import Downshift, { StateChangeOptions } from 'downshift';
import { autobind } from '../../utils/helper';
import { ICONS } from './IconPickerIcons';
import {
FormItem,
FormControlProps
} from './Item';
import keycode from 'keycode';
import Downshift, {StateChangeOptions} from 'downshift';
import {autobind} from '../../utils/helper';
import {ICONS} from './IconPickerIcons';
import {FormItem, FormControlProps} from './Item';
export interface Option {
label?: string;
value?: any;
};
}
export interface IconPickerProps extends FormControlProps {
placeholder?: string;
resetValue?: any;
noDataTip?: string;
};
}
export interface IconPickerState {
isOpen?: boolean;
inputValue?: string;
isFocused?: boolean;
vendorIndex?: number;
};
}
export default class IconPickerControl extends React.PureComponent<IconPickerProps, IconPickerState> {
input?:HTMLInputElement;
input?: HTMLInputElement;
state:IconPickerState = {
state: IconPickerState = {
isOpen: false,
inputValue: '',
isFocused: false,
vendorIndex: 0
};
static defaultProps:Pick<IconPickerProps, 'resetValue' | 'placeholder' | 'noDataTip'> = {
static defaultProps: Pick<IconPickerProps, 'resetValue' | 'placeholder' | 'noDataTip'> = {
resetValue: '',
placeholder: '',
noDataTip: '未找到匹配的图标'
};
componentWillReceiveProps(nextProps:IconPickerProps) {
componentWillReceiveProps(nextProps: IconPickerProps) {
const props = this.props;
if (props.value !== nextProps.value) {
@ -55,19 +52,20 @@ export default class IconPickerControl extends React.PureComponent<IconPickerPro
}
@autobind
changeVendor(index:number) {
this.setState({
vendorIndex: index
}, this.formatOptions);
changeVendor(index: number) {
this.setState(
{
vendorIndex: index
},
this.formatOptions
);
}
@autobind
formatOptions() {
let vendorIndex = this.state.vendorIndex || 0;
let {prefix, icons} = ICONS[vendorIndex];
return icons.map((icon:string) => (
{label: prefix + icon, value: prefix + icon}
));
return icons.map((icon: string) => ({label: prefix + icon, value: prefix + icon}));
}
@autobind
@ -76,9 +74,9 @@ export default class IconPickerControl extends React.PureComponent<IconPickerPro
}
@autobind
inputRef(ref:any) {
inputRef(ref: any) {
this.input = ref;
};
}
@autobind
focus() {
@ -101,7 +99,7 @@ export default class IconPickerControl extends React.PureComponent<IconPickerPro
}
@autobind
handleFocus(e:any) {
handleFocus(e: any) {
this.setState({
isOpen: true,
isFocused: true
@ -111,27 +109,25 @@ export default class IconPickerControl extends React.PureComponent<IconPickerPro
}
@autobind
handleBlur(e:any) {
const {
onBlur,
trimContents,
value,
onChange
} = this.props;
handleBlur(e: any) {
const {onBlur, trimContents, value, onChange} = this.props;
this.setState({
isFocused: false
}, () => {
if (trimContents && value && typeof value === "string") {
onChange(value.trim());
this.setState(
{
isFocused: false
},
() => {
if (trimContents && value && typeof value === 'string') {
onChange(value.trim());
}
}
});
);
onBlur && onBlur(e);
}
@autobind
handleInputChange(evt:React.ChangeEvent<HTMLInputElement>) {
handleInputChange(evt: React.ChangeEvent<HTMLInputElement>) {
let value = evt.currentTarget.value;
this.setState({
@ -140,15 +136,13 @@ export default class IconPickerControl extends React.PureComponent<IconPickerPro
}
@autobind
handleKeyDown(evt:React.KeyboardEvent<HTMLInputElement>) {
handleKeyDown(evt: React.KeyboardEvent<HTMLInputElement>) {
const code = keycode(evt.keyCode);
if (code !== 'backspace') {
return;
}
const {
onChange
} = this.props;
const {onChange} = this.props;
if (!this.state.inputValue) {
onChange('');
@ -159,7 +153,7 @@ export default class IconPickerControl extends React.PureComponent<IconPickerPro
}
@autobind
handleChange(value:any) {
handleChange(value: any) {
const {onChange} = this.props;
onChange(value);
@ -170,7 +164,7 @@ export default class IconPickerControl extends React.PureComponent<IconPickerPro
}
@autobind
handleStateChange(changes:StateChangeOptions<any>) {
handleStateChange(changes: StateChangeOptions<any>) {
switch (changes.type) {
case Downshift.stateChangeTypes.itemMouseEnter:
case Downshift.stateChangeTypes.changeInput:
@ -179,7 +173,7 @@ export default class IconPickerControl extends React.PureComponent<IconPickerPro
});
break;
default:
const state:IconPickerState = {};
const state: IconPickerState = {};
if (typeof changes.isOpen !== 'undefined') {
state.isOpen = changes.isOpen;
}
@ -194,19 +188,15 @@ export default class IconPickerControl extends React.PureComponent<IconPickerPro
}
renderFontIcons() {
const {
className,
inputOnly,
placeholder,
classnames: cx,
name,
value,
noDataTip,
formItem
} = this.props;
const {className, inputOnly, placeholder, classnames: cx, name, value, noDataTip, formItem} = this.props;
const options = this.formatOptions();
const vendors = this.getVendors();
const selectedOptions = formItem && formItem.selectedOptions.length ? formItem.selectedOptions : value ? [{label: value, value: value}] : [];
const selectedOptions =
formItem && formItem.selectedOptions.length
? formItem.selectedOptions
: value
? [{label: value, value: value}]
: [];
return (
<Downshift
@ -215,88 +205,110 @@ export default class IconPickerControl extends React.PureComponent<IconPickerPro
onChange={this.handleChange}
onOuterClick={this.handleBlur}
onStateChange={this.handleStateChange}
selectedItem={selectedOptions.map((option:Option) => option['value'])}
>{({
getInputProps,
getItemProps,
isOpen,
inputValue,
selectedItem
}) => {
let filteredOptions = inputValue && isOpen ? matchSorter(options, inputValue, {keys: ['label', 'value']}) : options;
filteredOptions = filteredOptions.filter((option:any) => !~selectedItem.indexOf(option.value));
selectedItem={selectedOptions.map((option: Option) => option['value'])}
>
{({getInputProps, getItemProps, isOpen, inputValue, selectedItem}) => {
let filteredOptions =
inputValue && isOpen ? matchSorter(options, inputValue, {keys: ['label', 'value']}) : options;
filteredOptions = filteredOptions.filter((option: any) => !~selectedItem.indexOf(option.value));
return (
<div
className={cx(`IconPickerControl-input IconPickerControl-input--withAC`, inputOnly ? className : '', {
'is-opened': isOpen
})}
onClick={this.handleClick}
>
<div className={cx("IconPickerControl-valueWrap")}>
{placeholder && !selectedOptions.length && !this.state.inputValue ? (
<div className={cx("IconPickerControl-placeholder")}>{placeholder}</div>
) : null}
return (
<div
className={cx(
`IconPickerControl-input IconPickerControl-input--withAC`,
inputOnly ? className : '',
{
'is-opened': isOpen
}
)}
onClick={this.handleClick}
>
<div className={cx('IconPickerControl-valueWrap')}>
{placeholder && !selectedOptions.length && !this.state.inputValue ? (
<div className={cx('IconPickerControl-placeholder')}>{placeholder}</div>
) : null}
{selectedOptions.map((option:Option, index:number) => inputValue && isOpen ? null : (
<div className={cx('IconPickerControl-value')} key={index}>
<i className={cx(`${option.value}`)}></i>
{option.label}
</div>
))}
<input
{...getInputProps({
name,
ref: this.inputRef,
onFocus: this.handleFocus,
onChange: this.handleInputChange,
onKeyDown: this.handleKeyDown
})}
autoComplete="off"
/>
</div>
{isOpen ? (
<div className={cx('IconPickerControl-sugsPanel')}>
{vendors.length > 1 ? <div className={cx('IconPickerControl-tabs')}>
{vendors.map((vendor:string, index: number) => (
<div
className={cx('IconPickerControl-tab', {'active': this.state.vendorIndex === index})}
onClick={() => this.changeVendor(index)}
key={index}
>
{vendor}
{selectedOptions.map((option: Option, index: number) =>
inputValue && isOpen ? null : (
<div className={cx('IconPickerControl-value')} key={index}>
<i className={cx(`${option.value}`)} />
{option.label}
</div>
))}
</div> : null}
)
)}
{filteredOptions.length ? <div className={cx('IconPickerControl-sugs', vendors.length > 1 ? 'IconPickerControl-multiVendor' : 'IconPickerControl-singleVendor')}>
{filteredOptions.map((option:Option, index:number) => (
<div
{...getItemProps({
item: option.value,
className: cx(`IconPickerControl-sugItem`)
})}
key={index}
>
<i className={cx(`${option.value}`)} title={`${option.value}`}></i>
</div>
))}
</div> : <div className={cx(vendors.length > 1 ? 'IconPickerControl-multiVendor' : 'IconPickerControl-singleVendor')}>{noDataTip}</div>}
<input
{...getInputProps({
name,
ref: this.inputRef,
onFocus: this.handleFocus,
onChange: this.handleInputChange,
onKeyDown: this.handleKeyDown
})}
autoComplete="off"
/>
</div>
) : null}
</div>
);
}}</Downshift>
{isOpen ? (
<div className={cx('IconPickerControl-sugsPanel')}>
{vendors.length > 1 ? (
<div className={cx('IconPickerControl-tabs')}>
{vendors.map((vendor: string, index: number) => (
<div
className={cx('IconPickerControl-tab', {
active: this.state.vendorIndex === index
})}
onClick={() => this.changeVendor(index)}
key={index}
>
{vendor}
</div>
))}
</div>
) : null}
{filteredOptions.length ? (
<div
className={cx(
'IconPickerControl-sugs',
vendors.length > 1
? 'IconPickerControl-multiVendor'
: 'IconPickerControl-singleVendor'
)}
>
{filteredOptions.map((option: Option, index: number) => (
<div
{...getItemProps({
item: option.value,
className: cx(`IconPickerControl-sugItem`)
})}
key={index}
>
<i className={cx(`${option.value}`)} title={`${option.value}`} />
</div>
))}
</div>
) : (
<div
className={cx(
vendors.length > 1
? 'IconPickerControl-multiVendor'
: 'IconPickerControl-singleVendor'
)}
>
{noDataTip}
</div>
)}
</div>
) : null}
</div>
);
}}
</Downshift>
);
}
render():JSX.Element {
const {
className,
classPrefix: ns,
inputOnly
} = this.props;
render(): JSX.Element {
const {className, classPrefix: ns, inputOnly} = this.props;
let input = this.renderFontIcons();
@ -305,9 +317,11 @@ export default class IconPickerControl extends React.PureComponent<IconPickerPro
}
return (
<div className={cx(className, `${ns}IconPickerControl`, {
'is-focused': this.state.isFocused
})}>
<div
className={cx(className, `${ns}IconPickerControl`, {
'is-focused': this.state.isFocused
})}
>
{input}
</div>
);
@ -317,4 +331,4 @@ export default class IconPickerControl extends React.PureComponent<IconPickerPro
@FormItem({
type: 'icon-picker'
})
export class IconPickerControlRenderer extends IconPickerControl {};
export class IconPickerControlRenderer extends IconPickerControl {}

View File

@ -2,16 +2,25 @@ interface Vendor {
name: string;
prefix: string;
icons: string[];
};
}
export let ICONS: Vendor[]= [
export let ICONS: Vendor[] = [
{
name: 'Font Awesome 4.7',
prefix: 'fa fa-',
icons: ["slideshare","snapchat","snapchat-ghost","snapchat-square","soundcloud","spotify","stack-exchange","stack-overflow"]
icons: [
'slideshare',
'snapchat',
'snapchat-ghost',
'snapchat-square',
'soundcloud',
'spotify',
'stack-exchange',
'stack-overflow'
]
}
]
];
export function setIconVendor(icons:Vendor[]) {
export function setIconVendor(icons: Vendor[]) {
ICONS = icons;
}
}

View File

@ -1,8 +1,5 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import Cropper from 'react-cropper';
import DropZone from 'react-dropzone';
@ -12,9 +9,9 @@ import 'blueimp-canvastoblob';
// import 'cropperjs/dist/cropper.css';
import find = require('lodash/find');
import qs from 'qs';
import { Payload } from '../../types';
import { filter } from '../../utils/tpl';
import { Switch } from '../../components';
import {Payload} from '../../types';
import {filter} from '../../utils/tpl';
import {Switch} from '../../components';
export interface ImageProps extends FormControlProps {
placeholder?: string;
@ -28,7 +25,7 @@ export interface ImageProps extends FormControlProps {
minHeight?: number;
aspectRatio?: number;
aspectRatioLabel?: string;
},
};
accept?: string;
btnUploadClassName?: string;
btnClassName?: string;
@ -38,7 +35,7 @@ export interface ImageProps extends FormControlProps {
delimiter?: string;
autoUpload?: boolean;
multiple?: boolean;
};
}
export interface ImageState {
uploading: boolean;
@ -49,16 +46,16 @@ export interface ImageState {
maxWidth?: number;
maxHeight?: number;
};
files: Array<FileValue|FileX>;
files: Array<FileValue | FileX>;
crop?: object;
error?: string;
cropFile?: FileValue;
submitOnChange?: boolean;
};
}
export interface FileValue {
value?: any;
state: 'init' | 'error' | 'pending' | 'uploading' | 'uploaded' | 'invalid',
state: 'init' | 'error' | 'pending' | 'uploading' | 'uploaded' | 'invalid';
url?: string;
error?: string;
info?: {
@ -66,13 +63,13 @@ export interface FileValue {
height: number;
len?: number;
};
[propName:string]: any;
[propName: string]: any;
}
export interface FileX extends File {
preview?: string;
state?: 'init' | 'error' | 'pending' | 'uploading' | 'uploaded' | 'invalid';
[propName:string]: any;
[propName: string]: any;
}
export default class ImageControl extends React.Component<ImageProps, ImageState> {
@ -92,7 +89,7 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
multiple: false
};
static formatFileSize(size:number | string, units = [' B', ' KB', ' M', ' G']) {
static formatFileSize(size: number | string, units = [' B', ' KB', ' M', ' G']) {
size = parseInt(size as string, 10) || 0;
while (size > 1024 && units.length > 1) {
@ -103,17 +100,21 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
return size.toFixed(2) + units[0];
}
static valueToFile(value:string | object, props?:ImageProps):FileValue|undefined {
return value ? {
...(typeof value === 'string' ? {
value,
url: value
} : value),
state: 'init'
} : undefined;
static valueToFile(value: string | object, props?: ImageProps): FileValue | undefined {
return value
? {
...(typeof value === 'string'
? {
value,
url: value
}
: value),
state: 'init'
}
: undefined;
}
static sizeInfo(width?:number, height?:number):string {
static sizeInfo(width?: number, height?: number): string {
if (!width) {
return `高度${height}px`;
} else if (!height) {
@ -123,7 +124,7 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
return `尺寸(${width} x ${height}`;
}
state:ImageState = {
state: ImageState = {
uploading: false,
locked: false,
compress: false,
@ -131,22 +132,27 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
compressOptions: {}
};
current: FileValue|FileX|null = null;
resolve?: (value?:any) => void;
current: FileValue | FileX | null = null;
resolve?: (value?: any) => void;
constructor(props:ImageProps) {
constructor(props: ImageProps) {
super(props);
const value:string|Array<string | FileValue>|FileValue = props.value;
const value: string | Array<string | FileValue> | FileValue = props.value;
const multiple = props.multiple;
const joinValues = props.joinValues;
const delimiter = props.delimiter as string;
let files:Array<FileValue> = [];
let files: Array<FileValue> = [];
if (value) {
// files = (multiple && Array.isArray(value) ? value : joinValues ? (value as string).split(delimiter) : [value])
files = (Array.isArray(value) ? value : (joinValues && typeof value === 'string' && multiple) ? (value as string).split(delimiter) : [value])
.map(item => ImageControl.valueToFile(item) as FileValue)
.filter(item => item);
files = (Array.isArray(value)
? value
: joinValues && typeof value === 'string' && multiple
? (value as string).split(delimiter)
: [value]
)
.map(item => ImageControl.valueToFile(item) as FileValue)
.filter(item => item);
}
this.state = {
@ -176,33 +182,38 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
this.handlePaste = this.handlePaste.bind(this);
}
componentWillReceiveProps(nextProps:ImageProps) {
componentWillReceiveProps(nextProps: ImageProps) {
const props = this.props;
if (props.value !== nextProps.value) {
const value:string|Array<string | FileValue>|FileValue = nextProps.value;
const value: string | Array<string | FileValue> | FileValue = nextProps.value;
const multiple = nextProps.multiple;
const joinValues = nextProps.joinValues;
const delimiter = nextProps.delimiter as string;
let files:Array<FileValue> = [];
let files: Array<FileValue> = [];
if (value) {
files = (Array.isArray(value) ? value : (joinValues && typeof value === 'string') ? (value as string).split(delimiter) : [value])
.map(item => {
let obj = ImageControl.valueToFile(item, nextProps) as FileValue;
let org;
files = (Array.isArray(value)
? value
: joinValues && typeof value === 'string'
? (value as string).split(delimiter)
: [value]
)
.map(item => {
let obj = ImageControl.valueToFile(item, nextProps) as FileValue;
let org;
if (obj && (org = find(this.state.files, item => (item as FileValue).value === obj.value))) {
obj = {
...org,
...obj
};
}
if (obj && (org = find(this.state.files, item => (item as FileValue).value === obj.value))) {
obj = {
...org,
...obj
};
}
return obj;
})
.filter(item => item);
return obj;
})
.filter(item => item);
}
this.setState({
@ -217,7 +228,7 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
}
}
buildCrop(props:ImageProps) {
buildCrop(props: ImageProps) {
let crop = props.crop;
if (crop && props.multiple) {
@ -244,7 +255,7 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
return crop;
}
handleDropRejected(rejectedFiles:any, evt:React.DragEvent<any>) {
handleDropRejected(rejectedFiles: any, evt: React.DragEvent<any>) {
evt.type === 'change' && alert('您选择的文件类型不符已被过滤!');
}
@ -253,17 +264,20 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
return;
}
this.setState({
uploading: true,
locked: true,
files: this.state.files.map(file => {
if (file.state === 'error') {
file.state = 'pending';
}
this.setState(
{
uploading: true,
locked: true,
files: this.state.files.map(file => {
if (file.state === 'error') {
file.state = 'pending';
}
return file;
})
}, this.tick);
return file;
})
},
this.tick
);
}
toggleUpload() {
@ -281,78 +295,94 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
}
tick() {
if (this.current || !this.state.uploading) {return;}
if (this.current || !this.state.uploading) {
return;
}
const file = find(this.state.files, item => item.state === 'pending');
if (file) {
this.current = file;
file.state = 'uploading';
this.setState({
files: this.state.files.concat()
}, () => this.sendFile(file as FileX, (error, file, obj) => {
const files = this.state.files.concat();
const idx = files.indexOf(file);
this.setState(
{
files: this.state.files.concat()
},
() =>
this.sendFile(file as FileX, (error, file, obj) => {
const files = this.state.files.concat();
const idx = files.indexOf(file);
if (!~idx) {
return;
}
if (!~idx) {
return;
}
let newFile:FileX | FileValue = file;
let newFile: FileX | FileValue = file;
if (error) {
newFile.state = file.state !== 'uploading' ? file.state : 'error';
newFile.error = error;
if (error) {
newFile.state = file.state !== 'uploading' ? file.state : 'error';
newFile.error = error;
if (!this.props.multiple && newFile.state === 'invalid') {
files.splice(idx, 1);
if (!this.props.multiple && newFile.state === 'invalid') {
files.splice(idx, 1);
this.current = null;
return this.setState(
{
files: files,
error: error
},
this.tick
);
}
} else {
newFile = obj as FileValue;
}
files.splice(idx, 1, newFile);
this.current = null;
return this.setState({
files: files,
error: error
}, this.tick);
}
} else {
newFile = obj as FileValue;
}
files.splice(idx, 1, newFile);
this.current = null;
this.setState({
files: files
}, this.tick);
}));
this.setState(
{
files: files
},
this.tick
);
})
);
} else {
this.setState({
uploading: false,
locked: false
}, () => {
this.onChange();
this.setState(
{
uploading: false,
locked: false
},
() => {
this.onChange();
if (this.resolve) {
this.resolve(this.state.files.some(file => file.state === 'error') ? '文件上传失败请重试' : null);
this.resolve = undefined;
if (this.resolve) {
this.resolve(
this.state.files.some(file => file.state === 'error') ? '文件上传失败请重试' : null
);
this.resolve = undefined;
}
}
});
);
}
}
removeFile(file:FileValue, index:number) {
removeFile(file: FileValue, index: number) {
const files = this.state.files.concat();
files.splice(index, 1);
this.setState({
files: files
}, this.onChange);
this.setState(
{
files: files
},
this.onChange
);
}
editImage(index:number) {
const {
multiple
} = this.props;
editImage(index: number) {
const {multiple} = this.props;
const files = this.state.files;
@ -362,27 +392,27 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
state: 'init'
}
});
}
onChange() {
const {
multiple,
onChange,
joinValues,
extractValue,
delimiter,
valueField
} = this.props;
const {multiple, onChange, joinValues, extractValue, delimiter, valueField} = this.props;
const files = this.state.files.filter(file => file.state == 'uploaded' || file.state == 'init');
let newValue:any = files.length ? joinValues ? files[0].value : files[0] : '';
let newValue: any = files.length ? (joinValues ? files[0].value : files[0]) : '';
if (multiple) {
newValue = joinValues ? files.map(item => item.value).join(delimiter) : extractValue ? files.map(item => item.value) : files;
newValue = joinValues
? files.map(item => item.value).join(delimiter)
: extractValue
? files.map(item => item.value)
: files;
} else {
newValue = joinValues ? newValue.value || newValue : extractValue ? newValue[valueField || 'value'] : newValue;
newValue = joinValues
? newValue.value || newValue
: extractValue
? newValue[valueField || 'value']
: newValue;
}
onChange(newValue);
@ -392,10 +422,10 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
this.refs.dropzone && (this.refs.dropzone as any).open();
}
handleDrop(files:Array<FileX>) {
handleDrop(files: Array<FileX>) {
const {multiple, crop} = this.props;
if (crop && !multiple ) {
if (crop && !multiple) {
return this.setState({
locked: true,
lockedReason: '请选择放弃或者应用',
@ -406,13 +436,13 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
this.addFiles(files);
}
handlePaste(e:React.ClipboardEvent<any>) {
handlePaste(e: React.ClipboardEvent<any>) {
const event = e.nativeEvent;
const files:Array<FileX> = [];
const files: Array<FileX> = [];
const items = event.clipboardData.items;
[].slice.call(items).forEach((item:DataTransferItem) => {
let blob:FileX;
[].slice.call(items).forEach((item: DataTransferItem) => {
let blob: FileX;
if (item.kind !== 'file' || !(blob = item.getAsFile() as File) || !/^image/i.test(blob.type)) {
return;
@ -426,7 +456,7 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
}
handleCrop() {
(this.refs.cropper as any).getCroppedCanvas().toBlob((file:File) => {
(this.refs.cropper as any).getCroppedCanvas().toBlob((file: File) => {
this.addFiles([file]);
this.setState({
cropFile: undefined,
@ -437,14 +467,17 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
}
cancelCrop() {
this.setState({
cropFile: undefined,
locked: false,
lockedReason: ''
}, this.onChange);
this.setState(
{
cropFile: undefined,
locked: false,
lockedReason: ''
},
this.onChange
);
}
addFiles(files:Array<FileX>) {
addFiles(files: Array<FileX>) {
if (!files.length) {
return;
}
@ -456,12 +489,17 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
currentFiles = [];
}
const allowed = (multiple ? maxLength ? maxLength : (files.length + currentFiles.length) : 1) - currentFiles.length;
const inputFiles:Array<FileX> = [];
const allowed =
(multiple ? (maxLength ? maxLength : files.length + currentFiles.length) : 1) - currentFiles.length;
const inputFiles: Array<FileX> = [];
[].slice.call(files, 0, allowed).forEach((file: FileX) => {
if (maxSize && file.size > maxSize) {
alert(`您选择的文件 ${file.name} 大小为 ${ImageControl.formatFileSize(file.size)} 超出了最大为 ${ImageControl.formatFileSize(maxSize)} 的限制,请重新选择`);
alert(
`您选择的文件 ${file.name} 大小为 ${ImageControl.formatFileSize(
file.size
)} ${ImageControl.formatFileSize(maxSize)} `
);
return;
}
@ -476,22 +514,23 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
return;
}
this.setState({
error: undefined,
files: currentFiles.concat(inputFiles),
locked: true
}, () => {
const {
autoUpload
} = this.props;
this.setState(
{
error: undefined,
files: currentFiles.concat(inputFiles),
locked: true
},
() => {
const {autoUpload} = this.props;
if (autoUpload) {
this.startUpload();
if (autoUpload) {
this.startUpload();
}
}
});
);
}
sendFile(file:FileX, cb:(error:null|string, file:FileX, obj?: FileValue) => void) {
sendFile(file: FileX, cb: (error: null | string, file: FileX, obj?: FileValue) => void) {
const {limit} = this.props;
if (!limit) {
@ -504,14 +543,21 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
const height = image.height;
let error = '';
if (limit.width && limit.width != width || limit.height && limit.height != height) {
if ((limit.width && limit.width != width) || (limit.height && limit.height != height)) {
error = `您选择的图片不符合尺寸要求, 请上传${ImageControl.sizeInfo(limit.width, limit.height)}的图片`;
} else if (limit.maxWidth && limit.maxWidth < width || limit.maxHeight && limit.maxHeight < height) {
error = `您选择的图片不符合尺寸要求, 请上传不要超过${ImageControl.sizeInfo(limit.maxWidth, limit.maxHeight)}的图片`;
} else if (limit.minWidth && limit.minWidth > width || limit.minHeight && limit.minHeight > height) {
error = `您选择的图片不符合尺寸要求, 请上传不要小于${ImageControl.sizeInfo(limit.minWidth, limit.minHeight)}的图片`;
} else if (limit.aspectRatio && Math.abs((width / height) - limit.aspectRatio) > 0.01) {
error = `您选择的图片不符合尺寸要求, 请上传尺寸比率为 ${limit.aspectRatioLabel || limit.aspectRatio} 的图片`
} else if ((limit.maxWidth && limit.maxWidth < width) || (limit.maxHeight && limit.maxHeight < height)) {
error = `您选择的图片不符合尺寸要求, 请上传不要超过${ImageControl.sizeInfo(
limit.maxWidth,
limit.maxHeight
)}`;
} else if ((limit.minWidth && limit.minWidth > width) || (limit.minHeight && limit.minHeight > height)) {
error = `您选择的图片不符合尺寸要求, 请上传不要小于${ImageControl.sizeInfo(
limit.minWidth,
limit.minHeight
)}`;
} else if (limit.aspectRatio && Math.abs(width / height - limit.aspectRatio) > 0.01) {
error = `您选择的图片不符合尺寸要求, 请上传尺寸比率为 ${limit.aspectRatioLabel ||
limit.aspectRatio} `;
}
if (error) {
@ -524,7 +570,7 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
image.src = (file.preview || file.url) as string;
}
_upload(file:Blob, cb:(error:null|string, file:Blob, obj?: FileValue) => void) {
_upload(file: Blob, cb: (error: null | string, file: Blob, obj?: FileValue) => void) {
let compressOptions = this.state.compressOptions;
if (this.props.showCompressOptions) {
@ -536,29 +582,29 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
}
this._send(file, this.props.reciever as string, {compress: this.state.compress, compressOptions})
.then((ret: Payload) => {
if (ret.status) {
throw new Error(ret.msg || '上传失败, 请重试');
}
.then((ret: Payload) => {
if (ret.status) {
throw new Error(ret.msg || '上传失败, 请重试');
}
const obj:FileValue = {
...ret.data,
state: 'uploaded'
};
obj.value = obj.value || obj.url;
const obj: FileValue = {
...ret.data,
state: 'uploaded'
};
obj.value = obj.value || obj.url;
cb(null, file, obj);
})
.catch(error => cb(error.message || '上传失败,请重试', file))
cb(null, file, obj);
})
.catch(error => cb(error.message || '上传失败,请重试', file));
}
_send(file:Blob, reciever:string, params:object):Promise<Payload> {
_send(file: Blob, reciever: string, params: object): Promise<Payload> {
const fd = new FormData();
const data = this.props.data;
reciever = filter(reciever, data);
const fileField = this.props.fileField || 'file';
fd.append(fileField, file, (file as File).name);
const idx = reciever.indexOf('?');
if (~idx && params) {
@ -591,7 +637,7 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
(this.refs.dropzone as any).open();
}
handleImageLoaded(index:number, e:React.UIEvent<any>) {
handleImageLoaded(index: number, e: React.UIEvent<any>) {
const imgDom = e.currentTarget;
const img = new Image();
img.onload = () => {
@ -612,18 +658,21 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
files.splice(index, 1, file);
const needUploading = !!(this.current || find(files, file => file.state === 'pending'));
this.setState({
files: files
}, !needUploading ? this.onChange : undefined);
this.setState(
{
files: files
},
!needUploading ? this.onChange : undefined
);
};
img.src = imgDom.src;
}
validate():any {
validate(): any {
if (this.state.locked && this.state.lockedReason) {
return this.state.lockedReason;
} else if (this.state.uploading || this.state.files.some(item => item.state === 'pending')) {
return new Promise((resolve) => {
return new Promise(resolve => {
this.resolve = resolve;
this.startUpload();
});
@ -643,43 +692,58 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
return (
<div key="options" className="m-t">
<Switch
classPrefix={classPrefix}
checked={!!this.state.compress}
onChange={checked => this.setState({compress: checked})}
disabled={this.props.disabled}
/>
<Switch
classPrefix={classPrefix}
checked={!!this.state.compress}
onChange={checked => this.setState({compress: checked})}
disabled={this.props.disabled}
/>
<span className="m-l-xs">?</span>
<span className="m-l-xs">?</span>
{this.state.compress && (
<div className="inline">
<input
className="form-control w-xs inline m-l-xs m-r-xs"
type="text"
value={
typeof this.state.compressOptions.maxWidth === 'undefined'
? 800
: this.state.compressOptions.maxWidth
}
onChange={e =>
this.setState({
compressOptions: {
...this.state.compressOptions,
maxWidth: parseInt(e.currentTarget.value, 10) || 0
}
})
}
disabled={this.props.disabled}
/>
{this.state.compress && (
<div className="inline">
<input
className="form-control w-xs inline m-l-xs m-r-xs"
type="text"
value={typeof this.state.compressOptions.maxWidth === 'undefined' ? 800 : this.state.compressOptions.maxWidth}
onChange={e => this.setState({compressOptions: {
...this.state.compressOptions,
maxWidth: parseInt(e.currentTarget.value, 10) || 0
}})}
disabled={this.props.disabled}
/>
<span className=" m-l-xs m-r-xs">X</span>
<span className=" m-l-xs m-r-xs">X</span>
<input
className="form-control w-xs inline m-l-xs m-r-xs"
type="text"
value={typeof this.state.compressOptions.maxHeight === 'undefined' ? 600 : this.state.compressOptions.maxHeight}
onChange={e => this.setState({compressOptions: {
...this.state.compressOptions,
maxHeight: parseInt(e.currentTarget.value, 10) || 0
}})}
disabled={this.props.disabled}
/>
</div>
)}
<input
className="form-control w-xs inline m-l-xs m-r-xs"
type="text"
value={
typeof this.state.compressOptions.maxHeight === 'undefined'
? 600
: this.state.compressOptions.maxHeight
}
onChange={e =>
this.setState({
compressOptions: {
...this.state.compressOptions,
maxHeight: parseInt(e.currentTarget.value, 10) || 0
}
})
}
disabled={this.props.disabled}
/>
</div>
)}
</div>
);
}
@ -699,41 +763,19 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
hideUploadButton
} = this.props;
const {
files,
error,
crop,
uploading,
cropFile
} = this.state;
const {files, error, crop, uploading, cropFile} = this.state;
const hasPending = files.some(file => file.state == 'pending');
return (
<div
className={cx(`${ns}ImageControl`, className)}
tabIndex={-1}
onPaste={this.handlePaste}
>
<div className={cx(`${ns}ImageControl`, className)} tabIndex={-1} onPaste={this.handlePaste}>
{cropFile ? (
<div className="cropper-wrapper">
<Cropper
{...crop}
ref="cropper"
src={cropFile.preview}
/>
<button
type="button"
className="btn-sm btn btn-link"
onClick={this.handleCrop}
>
<Cropper {...crop} ref="cropper" src={cropFile.preview} />
<button type="button" className="btn-sm btn btn-link" onClick={this.handleCrop}>
<i className="fa fa-2x fa-check text-warning" />
</button>
<button
type="button"
className="btn-sm btn btn-link"
onClick={this.cancelCrop}
>
<button type="button" className="btn-sm btn btn-link" onClick={this.cancelCrop}>
<i className="fa fa-2x fa-times text-white" />
</button>
</div>
@ -751,11 +793,13 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
disableClick
accept={accept}
multiple={multiple}
>
{files && files.length ? (
<div className={cx("image-list clearfix", {
>
{files && files.length ? (
<div
className={cx('image-list clearfix', {
'image-list-multiple': multiple
})}>
})}
>
{files.map((file, key) => (
<div
key={key}
@ -764,39 +808,92 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
invalid: file.state === 'error' || file.state == 'invalid'
})}
>
<div className="img-wrapper"><img onLoad={this.handleImageLoaded.bind(this, key)} src={file.url || file.preview} alt={file.name} className="img-rounded" /></div>
{file.info ? [
<p key="1">{file.info.width} x {file.info.height}</p>,
file.info.len ? (<p key="2">{ImageControl.formatFileSize(file.info.len)}</p>) : null
] : (<p>...</p>)}
<div className="img-wrapper">
<img
onLoad={this.handleImageLoaded.bind(this, key)}
src={file.url || file.preview}
alt={file.name}
className="img-rounded"
/>
</div>
{file.info ? (
[
<p key="1">
{file.info.width} x {file.info.height}
</p>,
file.info.len ? (
<p key="2">{ImageControl.formatFileSize(file.info.len)}</p>
) : null
]
) : (
<p>...</p>
)}
{file.error ? (<p className="text-danger">{file.error}</p>) : null}
{file.error ? <p className="text-danger">{file.error}</p> : null}
<div className="image-overlay">
{file.state === 'uploading' ? (<i className="fa fa-spinner fa-spin fa-2x fa-fw" />) : null}
{!disabled && file.state !== 'uploading' ? (<button onClick={this.removeFile.bind(this, file, key)} type="button" className={cx("close", {'crop-close' :!!crop})}><span>&times;</span></button>) : null}
{!!crop && !disabled && file.state !== 'uploading' ? (<button onClick={this.editImage.bind(this, key)} type="button" className="edit"><i className="fa fa-pencil"></i></button>) : null}
{!disabled && file.state !== 'uploading' ? (<a target="_blank" href={file.url || file.preview} className="view"><i className="fa fa-search"></i></a>) : null}
{file.state === 'uploading' ? (
<i className="fa fa-spinner fa-spin fa-2x fa-fw" />
) : null}
{!disabled && file.state !== 'uploading' ? (
<button
onClick={this.removeFile.bind(this, file, key)}
type="button"
className={cx('close', {'crop-close': !!crop})}
>
<span>&times;</span>
</button>
) : null}
{!!crop && !disabled && file.state !== 'uploading' ? (
<button
onClick={this.editImage.bind(this, key)}
type="button"
className="edit"
>
<i className="fa fa-pencil" />
</button>
) : null}
{!disabled && file.state !== 'uploading' ? (
<a target="_blank" href={file.url || file.preview} className="view">
<i className="fa fa-search" />
</a>
) : null}
</div>
</div>
))}
{multiple && (!maxLength || files.length < maxLength) || !multiple && !files.length ? (
<label className={cx("image-add-btn", {disabled})} onClick={this.handleSelect}>
<i className="fa fa-plus fa-3x" />
{(multiple && (!maxLength || files.length < maxLength)) ||
(!multiple && !files.length) ? (
<label className={cx('image-add-btn', {disabled})} onClick={this.handleSelect}>
<i className="fa fa-plus fa-3x" />
</label>
) : null}
</div>
) : (<div className={error ? 'text-danger' : undefined}>{error || placeholder}
<button type="button" className={cx('btn m-l-sm', btnClassName)} disabled={disabled} onClick={this.handleSelect}><i className="fa fa-cloud-upload" /> </button>
</div>)}
</div>
) : (
<div className={error ? 'text-danger' : undefined}>
{error || placeholder}
<button
type="button"
className={cx('btn m-l-sm', btnClassName)}
disabled={disabled}
onClick={this.handleSelect}
>
<i className="fa fa-cloud-upload" />
</button>
</div>
)}
</DropZone>
)}
{this.renderCompressOptions()}
{!autoUpload && !hideUploadButton && files.length ? (
<button type="button" className={cx('btn m-r-xs', btnUploadClassName)} disabled={!hasPending} onClick={this.toggleUpload}>
<button
type="button"
className={cx('btn m-r-xs', btnUploadClassName)}
disabled={!hasPending}
onClick={this.toggleUpload}
>
{uploading ? '暂停上传' : '开始上传'}
</button>
) : null}
@ -805,10 +902,8 @@ export default class ImageControl extends React.Component<ImageProps, ImageState
}
}
@FormItem({
type: 'image',
sizeMutable: false
})
export class ImageControlRenderer extends ImageControl {};
export class ImageControlRenderer extends ImageControl {}

View File

@ -1,29 +1,23 @@
import React from 'react';
import {
Renderer,
RendererProps
} from '../../factory';
import { Schema } from '../../types';
import {Renderer, RendererProps} from '../../factory';
import {Schema} from '../../types';
import Collapse from '../Collapse';
import {makeColumnClassBuild, makeHorizontalDeeper, isVisible, isDisabled} from '../../utils/helper';
import cx from 'classnames';
import getExprProperties from '../../utils/filter-schema';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
export interface InputGroupProps extends FormControlProps {
controls: Array<any>;
size?: 'xs' | 'sm' | 'normal'
};
size?: 'xs' | 'sm' | 'normal';
}
interface InputGroupState {
isFocused: boolean;
};
}
export class InputGroup extends React.Component<InputGroupProps, InputGroupState> {
constructor(props:InputGroupProps) {
constructor(props: InputGroupProps) {
super(props);
this.handleFocus = this.handleFocus.bind(this);
@ -37,7 +31,7 @@ export class InputGroup extends React.Component<InputGroupProps, InputGroupState
handleFocus() {
this.setState({
isFocused: true
})
});
}
handleBlur() {
@ -46,19 +40,20 @@ export class InputGroup extends React.Component<InputGroupProps, InputGroupState
});
}
renderControl(control:any, index:any, otherProps?: any) {
const {
render
} = this.props;
renderControl(control: any, index: any, otherProps?: any) {
const {render} = this.props;
if (!control) {
return null;
}
const subSchema: any = control && (control as Schema).type === 'control' ? control : {
type: 'control',
control
};
const subSchema: any =
control && (control as Schema).type === 'control'
? control
: {
type: 'control',
control
};
if (subSchema.control) {
let control = subSchema.control as Schema;
@ -72,14 +67,11 @@ export class InputGroup extends React.Component<InputGroupProps, InputGroupState
}
validate() {
const {
formStore,
controls
} = this.props;
const {formStore, controls} = this.props;
const errors:Array<string> = [];
const errors: Array<string> = [];
controls.forEach(({name})=> {
controls.forEach(({name}) => {
const formItem = name ? formStore.getItemByName(name) : null;
formItem && formItem.errors.length && errors.push(...formItem.errors);
});
@ -88,16 +80,7 @@ export class InputGroup extends React.Component<InputGroupProps, InputGroupState
}
render() {
let {
controls,
className,
mode,
horizontal,
formMode,
formHorizontal,
data,
classnames: cx
} = this.props;
let {controls, className, mode, horizontal, formMode, formHorizontal, data, classnames: cx} = this.props;
formMode = mode || formMode;
@ -116,13 +99,15 @@ export class InputGroup extends React.Component<InputGroupProps, InputGroupState
let horizontalDeeper = horizontal || makeHorizontalDeeper(formHorizontal, controls.length);
return (
<div
<div
className={cx(`InputGroup`, className, {
'is-focused': this.state.isFocused
})}
>
{controls.map((control, index) => {
const isAddOn = ~['icon', 'plain', 'tpl', 'button', 'submit', 'reset'].indexOf(control && control.type);
const isAddOn = ~['icon', 'plain', 'tpl', 'button', 'submit', 'reset'].indexOf(
control && control.type
);
let dom = this.renderControl(control, index, {
formHorizontal: horizontalDeeper,
@ -134,17 +119,28 @@ export class InputGroup extends React.Component<InputGroupProps, InputGroupState
});
return isAddOn ? (
<span key={index} className={cx(control.addOnclassName, ~['button', 'submit', 'reset'].indexOf(control && control.type) ? 'InputGroup-btn' : 'InputGroup-addOn')}>{dom}</span>
) : dom;
<span
key={index}
className={cx(
control.addOnclassName,
~['button', 'submit', 'reset'].indexOf(control && control.type)
? 'InputGroup-btn'
: 'InputGroup-addOn'
)}
>
{dom}
</span>
) : (
dom
);
})}
</div>
)
);
}
}
@FormItem({
type: 'input-group',
strictMode: false
})
export default class InputGroupRenderer extends InputGroup {}
export default class InputGroupRenderer extends InputGroup {}

View File

@ -1,21 +1,11 @@
import React from 'react';
import hoistNonReactStatic = require('hoist-non-react-statics');
import { IFormItemStore } from '../../store/form';
import { reaction } from 'mobx';
import {
RendererProps,
registerRenderer,
TestFunc,
RendererConfig,
HocStoreFactory
} from '../../factory';
import {
anyChanged,
ucFirst,
getWidthRate} from '../../utils/helper';
import { observer } from 'mobx-react';
import {IFormItemStore} from '../../store/form';
import {reaction} from 'mobx';
import {RendererProps, registerRenderer, TestFunc, RendererConfig, HocStoreFactory} from '../../factory';
import {anyChanged, ucFirst, getWidthRate} from '../../utils/helper';
import {observer} from 'mobx-react';
export interface FormItemBasicConfig extends Partial<RendererConfig> {
type?: string;
@ -33,8 +23,7 @@ export interface FormItemBasicConfig extends Partial<RendererConfig> {
// 兼容老用法,新用法直接在 Component 里面定义 validate 方法即可。
validate?: (values: any, value: any) => string | boolean;
};
}
export interface FormControlProps extends RendererProps {
// error string
@ -43,7 +32,7 @@ export interface FormControlProps extends RendererProps {
// error 详情
errors?: {
[propName: string]: string
[propName: string]: string;
};
defaultValue: any;
@ -57,18 +46,18 @@ export interface FormControlProps extends RendererProps {
formItem?: IFormItemStore;
strictMode?: boolean;
renderControl?: (props:RendererProps) => JSX.Element;
renderControl?: (props: RendererProps) => JSX.Element;
renderLabel?: boolean;
sizeMutable?: boolean;
wrap?: boolean;
hint?: string;
description?: string;
descriptionClassName?: string;
};
}
export interface FormControlState {
isFocused: boolean;
};
}
export type FormItemComponent = React.ComponentType<FormControlProps>;
export type FormControlComponent = React.ComponentType<FormControlProps>;
@ -79,8 +68,8 @@ export interface FormItemConfig extends FormItemBasicConfig {
export class FormItemWrap extends React.Component<FormControlProps, FormControlState> {
reaction: any;
constructor(props:FormControlProps) {
constructor(props: FormControlProps) {
super(props);
this.state = {
@ -92,9 +81,7 @@ export class FormItemWrap extends React.Component<FormControlProps, FormControlS
}
componentWillMount() {
const {
formItem: model
} = this.props;
const {formItem: model} = this.props;
if (model) {
this.reaction = reaction(() => model.errors.join(''), () => this.forceUpdate());
@ -105,14 +92,14 @@ export class FormItemWrap extends React.Component<FormControlProps, FormControlS
this.reaction && this.reaction();
}
handleFocus(e:any) {
handleFocus(e: any) {
this.setState({
isFocused: true
});
this.props.onFocus && this.props.onFocus(e);
}
handleBlur(e:any) {
handleBlur(e: any) {
this.setState({
isFocused: false
});
@ -141,14 +128,19 @@ export class FormItemWrap extends React.Component<FormControlProps, FormControlS
type,
classnames: cx,
formItem: model,
className: cx(`Form-control`, {
'is-inline': !!rest.inline,
'is-error': model && !model.valid,
[`Form-control--size${ucFirst(controlSize)}`]: sizeMutable !== false
&& typeof controlSize === 'string'
&& !!controlSize
&& controlSize !== 'full'
}, inputClassName)
className: cx(
`Form-control`,
{
'is-inline': !!rest.inline,
'is-error': model && !model.valid,
[`Form-control--size${ucFirst(controlSize)}`]:
sizeMutable !== false &&
typeof controlSize === 'string' &&
!!controlSize &&
controlSize !== 'full'
},
inputClassName
)
});
}
@ -187,62 +179,82 @@ export class FormItemWrap extends React.Component<FormControlProps, FormControlS
const right = getWidthRate(horizontal.right);
return (
<div className={cx(`Form-item Form-item--horizontal`, className, {
[`is-error`]: model && !model.valid,
[`is-required`]: required
})}>
<div
className={cx(`Form-item Form-item--horizontal`, className, {
[`is-error`]: model && !model.valid,
[`is-required`]: required
})}
>
{label !== false ? (
<label
className={cx(`Form-label`, {
[`Form-itemColumn--${typeof horizontal.leftFixed === 'string' ? horizontal.leftFixed : 'normal'}`]: horizontal.leftFixed,
[`Form-itemColumn--${left}`]: !horizontal.leftFixed
}, labelClassName)}
<label
className={cx(
`Form-label`,
{
[`Form-itemColumn--${
typeof horizontal.leftFixed === 'string' ? horizontal.leftFixed : 'normal'
}`]: horizontal.leftFixed,
[`Form-itemColumn--${left}`]: !horizontal.leftFixed
},
labelClassName
)}
>
<span>
{label}
{required ? (<span className={cx(`Form-star`)}>*</span>) : null}
{labelRemark ? render('label-remark', {
type: 'remark',
tooltip: labelRemark,
className: cx(`Form-labelRemark`),
container: env && env.getModalContainer ? env.getModalContainer() : undefined
}) : null}
{required ? <span className={cx(`Form-star`)}>*</span> : null}
{labelRemark
? render('label-remark', {
type: 'remark',
tooltip: labelRemark,
className: cx(`Form-labelRemark`),
container: env && env.getModalContainer ? env.getModalContainer() : undefined
})
: null}
</span>
</label>
) : null}
<div
<div
className={cx(`Form-value`, {
// [`Form-itemColumn--offset${getWidthRate(horizontal.offset)}`]: !label && label !== false,
[`Form-itemColumn--${right}`]: !!right && right !== (12 - left)
[`Form-itemColumn--${right}`]: !!right && right !== 12 - left
})}
>
{this.renderControl()}
{caption ? render('caption', caption, {
className: cx(`Form-caption`, captionClassName)
}) : null}
{caption
? render('caption', caption, {
className: cx(`Form-caption`, captionClassName)
})
: null}
{remark ? render('remark', {
type: 'remark',
tooltip: remark,
className: cx(`Form-remark`),
container: env && env.getModalContainer ? env.getModalContainer() : undefined
}) : null}
{remark
? render('remark', {
type: 'remark',
tooltip: remark,
className: cx(`Form-remark`),
container: env && env.getModalContainer ? env.getModalContainer() : undefined
})
: null}
{hint && this.state.isFocused
? render('hint', hint, {
className: cx(`Form-hint`)
})
: null}
{hint && this.state.isFocused ? (render('hint', hint, {
className: cx(`Form-hint`)
})) : null}
{model && !model.valid ? (
<ul className={cx(`Form-feedback`)}>
{model.errors.map((msg: string, key: number) => (<li key={key}>{msg}</li>))}
{model.errors.map((msg: string, key: number) => (
<li key={key}>{msg}</li>
))}
</ul>
) : null}
{description ? render('description', description, {
className: cx(`Form-description`, descriptionClassName)
}) : null}
{description
? render('description', description, {
className: cx(`Form-description`, descriptionClassName)
})
: null}
</div>
</div>
);
@ -273,51 +285,65 @@ export class FormItemWrap extends React.Component<FormControlProps, FormControlS
description = description || desc;
return (
<div className={cx(`Form-item Form-item--${formMode}`, className, {
'is-error': model && !model.valid,
[`is-required`]: required,
})}>
<div
className={cx(`Form-item Form-item--${formMode}`, className, {
'is-error': model && !model.valid,
[`is-required`]: required
})}
>
{label && renderLabel !== false ? (
<label className={cx(`Form-label`, labelClassName)}>
<span>
{label}
{required ? (<span className={cx(`Form-star`)}>*</span>) : null}
{labelRemark ? render('label-remark', {
type: 'remark',
tooltip: labelRemark,
className: cx(`Form-lableRemark`),
container: env && env.getModalContainer ? env.getModalContainer() : undefined
}) : null}
{required ? <span className={cx(`Form-star`)}>*</span> : null}
{labelRemark
? render('label-remark', {
type: 'remark',
tooltip: labelRemark,
className: cx(`Form-lableRemark`),
container: env && env.getModalContainer ? env.getModalContainer() : undefined
})
: null}
</span>
</label>
) : null}
{this.renderControl()}
{caption ? render('caption', caption, {
className: cx(`Form-caption`, captionClassName)
}) : null}
{caption
? render('caption', caption, {
className: cx(`Form-caption`, captionClassName)
})
: null}
{remark ? render('remark', {
type: 'remark',
className: cx(`Form-remark`),
tooltip: remark,
container: env && env.getModalContainer ? env.getModalContainer() : undefined
}) : null}
{remark
? render('remark', {
type: 'remark',
className: cx(`Form-remark`),
tooltip: remark,
container: env && env.getModalContainer ? env.getModalContainer() : undefined
})
: null}
{hint && this.state.isFocused ? (render('hint', hint, {
className: cx(`Form-hint`)
})) : null}
{hint && this.state.isFocused
? render('hint', hint, {
className: cx(`Form-hint`)
})
: null}
{model && !model.valid ? (
<ul className={cx(`Form-feedback`)}>
{model.errors.map((msg: string, key: number) => (<li key={key}>{msg}</li>))}
{model.errors.map((msg: string, key: number) => (
<li key={key}>{msg}</li>
))}
</ul>
) : null}
{description ? render('description', description, {
className: cx(`Form-description`, descriptionClassName)
}) : null}
{description
? render('description', description, {
className: cx(`Form-description`, descriptionClassName)
})
: null}
</div>
);
}
@ -346,21 +372,25 @@ export class FormItemWrap extends React.Component<FormControlProps, FormControlS
description = description || desc;
return (
<div className={cx(`Form-item Form-item--inline`, className, {
'is-error': model && !model.valid,
[`is-required`]: required,
})}>
{label && renderLabel !== false? (
<div
className={cx(`Form-item Form-item--inline`, className, {
'is-error': model && !model.valid,
[`is-required`]: required
})}
>
{label && renderLabel !== false ? (
<label className={cx(`Form-label`, labelClassName)}>
<span>
{label}
{required ? (<span className={cx(`Form-star`)}>*</span>) : null}
{labelRemark ? render('label-remark', {
type: 'remark',
tooltip: labelRemark,
className: cx(`Form-lableRemark`),
container: env && env.getModalContainer ? env.getModalContainer() : undefined
}) : null}
{required ? <span className={cx(`Form-star`)}>*</span> : null}
{labelRemark
? render('label-remark', {
type: 'remark',
tooltip: labelRemark,
className: cx(`Form-lableRemark`),
container: env && env.getModalContainer ? env.getModalContainer() : undefined
})
: null}
</span>
</label>
) : null}
@ -368,32 +398,41 @@ export class FormItemWrap extends React.Component<FormControlProps, FormControlS
<div className={cx(`Form-value`)}>
{this.renderControl()}
{caption ? render('caption', caption, {
className: cx(`Form-caption`, captionClassName)
}) : null}
{caption
? render('caption', caption, {
className: cx(`Form-caption`, captionClassName)
})
: null}
{remark ? render('remark', {
type: 'remark',
className: cx(`Form-remark`),
tooltip: remark,
container: env && env.getModalContainer ? env.getModalContainer() : undefined
}) : null}
{remark
? render('remark', {
type: 'remark',
className: cx(`Form-remark`),
tooltip: remark,
container: env && env.getModalContainer ? env.getModalContainer() : undefined
})
: null}
{hint && this.state.isFocused ? (render('hint', hint, {
className: cx(`Form-hint`)
})) : null}
{hint && this.state.isFocused
? render('hint', hint, {
className: cx(`Form-hint`)
})
: null}
{model && !model.valid ? (
<ul className={cx(`Form-value`)}>
{model.errors.map((msg: string, key: number) => (<li key={key}>{msg}</li>))}
{model.errors.map((msg: string, key: number) => (
<li key={key}>{msg}</li>
))}
</ul>
) : null}
{description ? render('description', description, {
className: cx(`Form-description`, descriptionClassName)
}) : null}
{description
? render('description', description, {
className: cx(`Form-description`, descriptionClassName)
})
: null}
</div>
</div>
);
}
@ -423,72 +462,85 @@ export class FormItemWrap extends React.Component<FormControlProps, FormControlS
description = description || desc;
return (
<div className={cx(`Form-item Form-item--${formMode}`, className, {
'is-error': model && !model.valid,
[`is-required`]: required,
})}>
<div
className={cx(`Form-item Form-item--${formMode}`, className, {
'is-error': model && !model.valid,
[`is-required`]: required
})}
>
<div className={cx('Form-rowInner')}>
{label && renderLabel !== false ? (
<label className={cx(`Form-label`, labelClassName)}>
<span>
{label}
{required ? (<span className={cx(`Form-star`)}>*</span>) : null}
{labelRemark ? render('label-remark', {
type: 'remark',
tooltip: labelRemark,
className: cx(`Form-lableRemark`),
container: env && env.getModalContainer ? env.getModalContainer() : undefined
}) : null}
{required ? <span className={cx(`Form-star`)}>*</span> : null}
{labelRemark
? render('label-remark', {
type: 'remark',
tooltip: labelRemark,
className: cx(`Form-lableRemark`),
container: env && env.getModalContainer ? env.getModalContainer() : undefined
})
: null}
</span>
</label>
) : null}
{this.renderControl()}
{caption ? render('caption', caption, {
className: cx(`Form-caption`, captionClassName)
}) : null}
{caption
? render('caption', caption, {
className: cx(`Form-caption`, captionClassName)
})
: null}
{remark ? render('remark', {
type: 'remark',
className: cx(`Form-remark`),
tooltip: remark,
container: env && env.getModalContainer ? env.getModalContainer() : undefined
}) : null}
{remark
? render('remark', {
type: 'remark',
className: cx(`Form-remark`),
tooltip: remark,
container: env && env.getModalContainer ? env.getModalContainer() : undefined
})
: null}
</div>
{hint && this.state.isFocused ? (render('hint', hint, {
className: cx(`Form-hint`)
})) : null}
{hint && this.state.isFocused
? render('hint', hint, {
className: cx(`Form-hint`)
})
: null}
{model && !model.valid ? (
<ul className={cx('Form-feedback')}>
{model.errors.map((msg: string, key: number) => (<li key={key}>{msg}</li>))}
{model.errors.map((msg: string, key: number) => (
<li key={key}>{msg}</li>
))}
</ul>
) : null}
{description ? render('description', description, {
className: cx(`Form-description`, descriptionClassName)
}) : null}
{description
? render('description', description, {
className: cx(`Form-description`, descriptionClassName)
})
: null}
</div>
);
}
render() {
const {
formMode,
inputOnly,
wrap
} = this.props;
const {formMode, inputOnly, wrap} = this.props;
if (wrap === false || inputOnly) {
return this.renderControl();
}
return formMode === 'inline'
? this.renderInline() : formMode === 'horizontal'
? this.renderHorizontal() : formMode === 'row'
? this.renderRow() : this.renderNormal();
? this.renderInline()
: formMode === 'horizontal'
? this.renderHorizontal()
: formMode === 'row'
? this.renderRow()
: this.renderNormal();
}
}
@ -498,7 +550,7 @@ export function registerFormItem(config: FormItemConfig): RendererConfig {
// 兼容老的 FormItem 用法。
if (config.validate && !Control.prototype.validate) {
const fn = config.validate;
Control.prototype.validate = function () {
Control.prototype.validate = function() {
// console.warn('推荐直接在类中定义,而不是 FormItem HOC 的参数中传入。');
const host = {
input: this
@ -507,7 +559,9 @@ export function registerFormItem(config: FormItemConfig): RendererConfig {
return fn.apply(host, arguments);
};
} else if (config.validate) {
console.error('FormItem配置中的 validate 将不起作用,因为类的成员函数中已经定义了 validate 方法,将优先使用类里面的实现。');
console.error(
'FormItem配置中的 validate 将不起作用,因为类的成员函数中已经定义了 validate 方法,将优先使用类里面的实现。'
);
}
if (config.storeType) {
@ -548,11 +602,8 @@ export function registerFormItem(config: FormItemConfig): RendererConfig {
}
componentWillMount() {
const {
validations,
formItem: model
} = this.props;
const {validations, formItem: model} = this.props;
// 组件注册的时候可能默认指定验证器类型
if (model && !validations && config.validations) {
model.config({
@ -567,68 +618,74 @@ export function registerFormItem(config: FormItemConfig): RendererConfig {
if (nextProps.strictMode === false) {
return true;
}
// 把可能会影响视图的白名单弄出来,减少重新渲染次数。
if (anyChanged([
'formPristine',
'addable',
'addButtonClassName',
'addButtonText',
'addOn',
'btnClassName',
'btnLabel',
'btnDisabled',
'className',
'clearable',
'columns',
'columnsCount',
'controls',
'desc',
'description',
'disabled',
'draggable',
'editable',
'editButtonClassName',
'formHorizontal',
'formMode',
'hideRoot',
'horizontal',
'icon',
'inline',
'inputClassName',
'label',
'labelClassName',
'labelField',
'language',
'level',
'max',
'maxRows',
'min',
'minRows',
'multiLine',
'multiple',
'option',
'placeholder',
'removable',
'required',
'remark',
'hint',
'rows',
'searchable',
'showCompressOptions',
'size',
'step',
'showInput',
'unit',
'value',
'diffValue'
], this.props, nextProps)) {
if (
anyChanged(
[
'formPristine',
'addable',
'addButtonClassName',
'addButtonText',
'addOn',
'btnClassName',
'btnLabel',
'btnDisabled',
'className',
'clearable',
'columns',
'columnsCount',
'controls',
'desc',
'description',
'disabled',
'draggable',
'editable',
'editButtonClassName',
'formHorizontal',
'formMode',
'hideRoot',
'horizontal',
'icon',
'inline',
'inputClassName',
'label',
'labelClassName',
'labelField',
'language',
'level',
'max',
'maxRows',
'min',
'minRows',
'multiLine',
'multiple',
'option',
'placeholder',
'removable',
'required',
'remark',
'hint',
'rows',
'searchable',
'showCompressOptions',
'size',
'step',
'showInput',
'unit',
'value',
'diffValue'
],
this.props,
nextProps
)
) {
return true;
}
return false;
}
getWrappedInstance() {
return this.ref;
}
@ -650,7 +707,7 @@ export function registerFormItem(config: FormItemConfig): RendererConfig {
} = this.props;
const controlSize = size || defaultSize;
return (
<Control
{...rest}
@ -661,14 +718,19 @@ export function registerFormItem(config: FormItemConfig): RendererConfig {
classnames={cx}
ref={this.refFn}
formItem={model}
className={cx(`Form-control`, {
'is-inline': !!rest.inline,
'is-error': model && !model.valid,
[`Form-control--size${ucFirst(controlSize)}`]: config.sizeMutable !== false
&& typeof controlSize === 'string'
&& !!controlSize
&& controlSize !== 'full'
}, inputClassName)}
className={cx(
`Form-control`,
{
'is-inline': !!rest.inline,
'is-error': model && !model.valid,
[`Form-control--size${ucFirst(controlSize)}`]:
config.sizeMutable !== false &&
typeof controlSize === 'string' &&
!!controlSize &&
controlSize !== 'full'
},
inputClassName
)}
/>
);
}
@ -687,14 +749,14 @@ export function registerFormItem(config: FormItemConfig): RendererConfig {
}
export function FormItem(config: FormItemBasicConfig) {
return function (component: FormControlComponent): any {
return function(component: FormControlComponent): any {
const renderer = registerFormItem({
...config,
component
});
return renderer.component as any;
}
};
}
export default FormItem;

View File

@ -1,49 +1,36 @@
import React from 'react';
import {
OptionsControl,
OptionsControlProps,
Option
} from './Options';
import { Schema } from '../../types';
import { createObject, isEmpty } from '../../utils/helper';
import {OptionsControl, OptionsControlProps, Option} from './Options';
import {Schema} from '../../types';
import {createObject, isEmpty} from '../../utils/helper';
import {dataMapping} from '../../utils/tpl-builtin';
export interface ListProps extends OptionsControlProps {
imageClassName: string;
submitOnDBClick?: boolean;
itemSchema?: Schema;
};
}
export default class ListControl extends React.Component<ListProps, any> {
static propsList = [
'itemSchema',
'value',
'renderFormItems'
];
static propsList = ['itemSchema', 'value', 'renderFormItems'];
static defaultProps = {
clearable: false,
imageClassName: '',
submitOnDBClick: false
}
};
handleDBClick(option:Option, e:React.MouseEvent<HTMLElement>) {
handleDBClick(option: Option, e: React.MouseEvent<HTMLElement>) {
this.props.onToggle(option);
this.props.onAction(e, {
type: 'submit'
});
}
handleClick(option:Option, e:React.MouseEvent<HTMLElement>) {
handleClick(option: Option, e: React.MouseEvent<HTMLElement>) {
if (e.target && (e.target as HTMLElement).closest('a,button')) {
return;
}
const {
onToggle,
multiple,
autoFill,
onBulkChange
} = this.props;
const {onToggle, multiple, autoFill, onBulkChange} = this.props;
const sendTo = !multiple && autoFill && !isEmpty(autoFill) && dataMapping(autoFill, option as Option);
sendTo && onBulkChange(sendTo);
@ -72,11 +59,11 @@ export default class ListControl extends React.Component<ListProps, any> {
data
} = this.props;
let body:JSX.Element | null = null;
let body: JSX.Element | null = null;
if (options && options.length) {
body = (
<div className={cx('ListControl-items',)}>
<div className={cx('ListControl-items')}>
{options.map((option, key) => (
<div
key={key}
@ -87,13 +74,25 @@ export default class ListControl extends React.Component<ListProps, any> {
onClick={this.handleClick.bind(this, option)}
onDoubleClick={submitOnDBClick ? this.handleDBClick.bind(this, option) : undefined}
>
{itemSchema ? render(`${key}/body`, itemSchema, {
data: createObject(data, option)
}) : option.body ? render(`${key}/body`, option.body) : [
option.image ? (<div key="image" className={cx('ListControl-itemImage', imageClassName)}><img src={option.image} alt={option.label} /></div>) : null,
option.label ? (<div key="label" className={cx('ListControl-itemLabel')}>{option.label}</div>) : null,
// {/* {option.tip ? (<div className={`${ns}ListControl-tip`}>{option.tip}</div>) : null} */}
]}
{itemSchema
? render(`${key}/body`, itemSchema, {
data: createObject(data, option)
})
: option.body
? render(`${key}/body`, option.body)
: [
option.image ? (
<div key="image" className={cx('ListControl-itemImage', imageClassName)}>
<img src={option.image} alt={option.label} />
</div>
) : null,
option.label ? (
<div key="label" className={cx('ListControl-itemLabel')}>
{option.label}
</div>
) : null
// {/* {option.tip ? (<div className={`${ns}ListControl-tip`}>{option.tip}</div>) : null} */}
]}
</div>
))}
</div>
@ -112,5 +111,4 @@ export default class ListControl extends React.Component<ListProps, any> {
type: 'list',
sizeMutable: false
})
export class ListControlRenderer extends ListControl {};
export class ListControlRenderer extends ListControl {}

View File

@ -1,22 +1,22 @@
/**
* @file filter
* @author fex
*/
* @file filter
* @author fex
*/
import React from 'react';
import cx from 'classnames';
import { FormControlProps, FormItem } from './Item';
import { buildApi, isValidApi, isEffectiveApi } from '../../utils/api';
import { Checkbox } from '../../components';
import {FormControlProps, FormItem} from './Item';
import {buildApi, isValidApi, isEffectiveApi} from '../../utils/api';
import {Checkbox} from '../../components';
export interface Column {
label: string;
[propName:string]: any;
[propName: string]: any;
}
export interface Row {
label: string;
[propName:string]: any;
[propName: string]: any;
}
export interface ValueItem extends Column, Row {
@ -24,31 +24,30 @@ export interface ValueItem extends Column, Row {
}
export interface MatrixProps extends FormControlProps {
columns: Array<Column>;
columns: Array<Column>;
rows: Array<Row>;
multiple: boolean;
};
}
export interface MatrixState {
columns: Array<Column>;
columns: Array<Column>;
rows: Array<Row>;
loading: boolean;
error?: string;
singleSelectMode?: 'cell' | 'row' | 'column';
};
}
export default class MatrixCheckbox extends React.Component<MatrixProps, MatrixState> {
static defaultProps:Partial<MatrixProps> = {
static defaultProps: Partial<MatrixProps> = {
columns: [],
rows: [],
multiple: true,
singleSelectMode: 'column' // multiple 为 false 时有效。
};
state:MatrixState;
sourceInvalid:boolean = false;
constructor(props:MatrixProps) {
state: MatrixState;
sourceInvalid: boolean = false;
constructor(props: MatrixProps) {
super(props);
this.state = {
@ -62,15 +61,12 @@ export default class MatrixCheckbox extends React.Component<MatrixProps, MatrixS
}
componentDidMount() {
const {
formInited,
addHook
} = this.props;
const {formInited, addHook} = this.props;
formInited ? this.reload() : addHook(this.reload, 'init')
formInited ? this.reload() : addHook(this.reload, 'init');
}
componentWillReceiveProps(nextProps:MatrixProps) {
componentWillReceiveProps(nextProps: MatrixProps) {
const props = this.props;
if (props.columns !== nextProps.columns || props.rows !== nextProps.rows) {
@ -97,12 +93,7 @@ export default class MatrixCheckbox extends React.Component<MatrixProps, MatrixS
}
reload() {
const {
source,
data,
env,
onChange
} = this.props;
const {source, data, env, onChange} = this.props;
if (!isEffectiveApi(source, data) || this.state.loading) {
return;
@ -113,45 +104,47 @@ export default class MatrixCheckbox extends React.Component<MatrixProps, MatrixS
}
// 需要联动加载吗?我看不一定会用到,先这样吧。
this.setState({
loading: true,
}, () => {
env.fetcher(source, data)
.then(ret => {
if (!ret.ok) {
throw new Error(ret.msg || '数据请求错误');
}
this.setState({
loading: false,
rows: (ret.data as any).rows || [],
columns: (ret.data as any).columns || [],
}, () => {
let value = (ret.data as any).value;
if (value) {
value = mergeValue(value, this.state.columns, this.state.rows);
onChange(value);
this.setState(
{
loading: true
},
() => {
env.fetcher(source, data)
.then(ret => {
if (!ret.ok) {
throw new Error(ret.msg || '数据请求错误');
}
});
})
.catch(reason => this.setState({
error: reason,
loading: false
}));
})
this.setState(
{
loading: false,
rows: (ret.data as any).rows || [],
columns: (ret.data as any).columns || []
},
() => {
let value = (ret.data as any).value;
if (value) {
value = mergeValue(value, this.state.columns, this.state.rows);
onChange(value);
}
}
);
})
.catch(reason =>
this.setState({
error: reason,
loading: false
})
);
}
);
}
toggleItem(checked:boolean, x:number, y:number) {
const {
columns,
rows,
} = this.state;
const {
multiple,
singleSelectMode
} = this.props;
toggleItem(checked: boolean, x: number, y: number) {
const {columns, rows} = this.state;
const {multiple, singleSelectMode} = this.props;
const value = this.props.value || buildDefaultValue(columns, rows);
if (multiple) {
value[x][y] = {
...value[x][y],
@ -164,14 +157,15 @@ export default class MatrixCheckbox extends React.Component<MatrixProps, MatrixS
checked: x === x2 ? checked : !checked
};
}
} else if (singleSelectMode === 'column') {
} else if (singleSelectMode === 'column') {
for (let y2 = 0, len = rows.length; y2 < len; y2++) {
value[x][y2] = {
...value[x][y2],
checked: y === y2 ? checked : !checked
};
}
} else { // 只剩下 cell 了
} else {
// 只剩下 cell 了
for (let y2 = 0, len = rows.length; y2 < len; y2++) {
for (let x2 = 0, len2 = columns.length; x2 < len2; x2++) {
value[x2][y2] = {
@ -186,50 +180,47 @@ export default class MatrixCheckbox extends React.Component<MatrixProps, MatrixS
}
renderInput() {
const {
columns,
rows
} = this.state;
const {
rowLabel,
className,
classnames: cx,
multiple
} = this.props;
const {columns, rows} = this.state;
const {rowLabel, className, classnames: cx, multiple} = this.props;
const value = this.props.value || buildDefaultValue(columns, rows);
return (
<div className={cx("Table m-b-none")}>
<div className={cx("Table-content")}>
<table className={cx("Table-table")}>
<div className={cx('Table m-b-none')}>
<div className={cx('Table-content')}>
<table className={cx('Table-table')}>
<thead>
<tr>
<th>{rowLabel}</th>
{columns.map((column, x) => (
<th key={x} className="text-center">{column.label}</th>
<th key={x} className="text-center">
{column.label}
</th>
))}
</tr>
</thead>
<tbody>
{rows.map((row, y) => (
<tr key={y}>
<td>
{row.label}{row.description || row.desc ? (
<span className="m-l-xs text-muted text-xs">{row.description || row.desc}</span>
) : null}
</td>
{columns.map((column, x) => (
<td key={x} className="text-center">
<Checkbox
type={multiple ? 'checkbox' : 'radio'}
checked={!!(value[x] && value[x][y] && value[x][y].checked)}
onChange={(checked:boolean) => this.toggleItem(checked, x, y)}
/>
{rows.map((row, y) => (
<tr key={y}>
<td>
{row.label}
{row.description || row.desc ? (
<span className="m-l-xs text-muted text-xs">
{row.description || row.desc}
</span>
) : null}
</td>
))}
</tr>
))}
{columns.map((column, x) => (
<td key={x} className="text-center">
<Checkbox
type={multiple ? 'checkbox' : 'radio'}
checked={!!(value[x] && value[x][y] && value[x][y].checked)}
onChange={(checked: boolean) => this.toggleItem(checked, x, y)}
/>
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
@ -238,30 +229,25 @@ export default class MatrixCheckbox extends React.Component<MatrixProps, MatrixS
}
render() {
const {
className,
render,
classnames: cx
} = this.props;
const {className, render, classnames: cx} = this.props;
const {
error,
loading
} = this.state;
const {error, loading} = this.state;
return (
<div key="input" className={cx('MatrixControl', className || '')}>
{error ? (
<div className={cx("MatrixControl-error Alert Alert--danger")}>
{String(error)}
</div>
) : this.renderInput()}
<div className={cx('MatrixControl-error Alert Alert--danger')}>{String(error)}</div>
) : (
this.renderInput()
)}
{loading ? render('loading', {
type: 'spinner',
overlay: true,
size: 'lg',
}):null}
{loading
? render('loading', {
type: 'spinner',
overlay: true,
size: 'lg'
})
: null}
</div>
);
}
@ -276,19 +262,23 @@ function buildDefaultValue(columns: Array<Column>, rows: Array<Row>): Array<Arra
rows = [];
}
return columns.map(column => rows.map(row => ({
...row,
...column,
checked: false
})));
return columns.map(column =>
rows.map(row => ({
...row,
...column,
checked: false
}))
);
}
function mergeValue(value:Array<Array<ValueItem>>, columns: Array<Column>, rows: Array<Row>):Array<Array<ValueItem>> {
return value.map((column, x) => column.map((item, y) => ({
...columns[x],
...rows[y],
...item
})));
function mergeValue(value: Array<Array<ValueItem>>, columns: Array<Column>, rows: Array<Row>): Array<Array<ValueItem>> {
return value.map((column, x) =>
column.map((item, y) => ({
...columns[x],
...rows[y],
...item
}))
);
}
@FormItem({
@ -296,4 +286,4 @@ function mergeValue(value:Array<Array<ValueItem>>, columns: Array<Column>, rows:
strictMode: false,
sizeMutable: false
})
export class MatrixRenderer extends MatrixCheckbox {};
export class MatrixRenderer extends MatrixCheckbox {}

View File

@ -1,6 +1,6 @@
import React from 'react';
import xorBy = require('lodash/xorBy')
import unionBy = require('lodash/unionBy')
import xorBy = require('lodash/xorBy');
import unionBy = require('lodash/unionBy');
import Overlay from '../../components/Overlay';
import Checkbox from '../../components/Checkbox';
import PopOver from '../../components/PopOver';
@ -9,11 +9,7 @@ import {Icon} from '../../components/icons';
import {autobind, flattenTree, isEmpty} from '../../utils/helper';
import {dataMapping} from '../../utils/tpl-builtin';
import {
OptionsControl,
OptionsControlProps,
Option
} from '../Form/Options'
import {OptionsControl, OptionsControlProps, Option} from '../Form/Options';
export interface NestedSelectProps extends OptionsControlProps {
cascade?: boolean;
@ -24,16 +20,16 @@ export interface NestedSelectState {
isOpened?: boolean;
}
export default class NestedSelectControl extends React.Component<NestedSelectProps, NestedSelectState>{
export default class NestedSelectControl extends React.Component<NestedSelectProps, NestedSelectState> {
static defaultProps: Partial<NestedSelectProps> = {
cascade: false,
withChildren: false
}
};
target: any;
alteredOptions: any;
state = {
isOpened: false
}
};
@autobind
domRef(ref: any) {
@ -57,12 +53,7 @@ export default class NestedSelectControl extends React.Component<NestedSelectPro
}
renderValue() {
const {
multiple,
classnames: cx,
selectedOptions,
labelField
} = this.props;
const {multiple, classnames: cx, selectedOptions, labelField} = this.props;
const len = Array.isArray(selectedOptions) ? selectedOptions.length : 0;
return (
<div className={cx('NestedSelect-valueWrap')} onClick={this.open}>
@ -76,37 +67,24 @@ export default class NestedSelectControl extends React.Component<NestedSelectPro
}
renderClear() {
const {
clearable,
value,
disabled,
classnames: cx
} = this.props;
const {clearable, value, disabled, classnames: cx} = this.props;
return clearable && !disabled && (Array.isArray(value) ? value.length : value) ?
(<a onClick={this.clearValue} className={cx('NestedSelect-clear')}><Icon icon="close" className="icon" /></a>) : null;
return clearable && !disabled && (Array.isArray(value) ? value.length : value) ? (
<a onClick={this.clearValue} className={cx('NestedSelect-clear')}>
<Icon icon="close" className="icon" />
</a>
) : null;
}
@autobind
clearValue() {
const {
onChange,
resetValue
} = this.props;
const {onChange, resetValue} = this.props;
onChange(typeof resetValue === 'undefined' ? '' : resetValue);
}
handleOptionClick(option: Option, e: React.MouseEvent<HTMLElement>) {
const {
multiple,
onChange,
joinValues,
extractValue,
valueField,
autoFill,
onBulkChange
} = this.props;
const {multiple, onChange, joinValues, extractValue, valueField, autoFill, onBulkChange} = this.props;
e.stopPropagation();
@ -188,15 +166,7 @@ export default class NestedSelectControl extends React.Component<NestedSelectPro
}
renderOptions(newOptions: Array<any>, isChildren: boolean, uncheckable: boolean): any {
const {
multiple,
selectedOptions,
classnames: cx,
value,
options,
disabled,
cascade
} = this.props;
const {multiple, selectedOptions, classnames: cx, value, options, disabled, cascade} = this.props;
if (multiple) {
let partialChecked = this.partialChecked(options);
@ -230,10 +200,20 @@ export default class NestedSelectControl extends React.Component<NestedSelectPro
>
{option.label}
</Checkbox>
{option.children ? (<div className={cx('NestedSelect-optionArrowRight')}><Icon icon="right-arrow" className="icon" /></div>) : null}
{option.children && option.children.length ? this.renderOptions(option.children, true, cascade ? false : uncheckable || multiple && checked) : null}
{option.children ? (
<div className={cx('NestedSelect-optionArrowRight')}>
<Icon icon="right-arrow" className="icon" />
</div>
) : null}
{option.children && option.children.length
? this.renderOptions(
option.children,
true,
cascade ? false : uncheckable || (multiple && checked)
)
: null}
</div>
)
);
})}
</div>
);
@ -250,28 +230,26 @@ export default class NestedSelectControl extends React.Component<NestedSelectPro
onClick={this.handleOptionClick.bind(this, option)}
>
<span>{option.label}</span>
{option.children ? (<div className={cx('NestedSelect-optionArrowRight')}><Icon icon="right-arrow" className="icon" /></div>) : null}
{option.children && option.children.length ? this.renderOptions(option.children, true, false) : null}
{option.children ? (
<div className={cx('NestedSelect-optionArrowRight')}>
<Icon icon="right-arrow" className="icon" />
</div>
) : null}
{option.children && option.children.length
? this.renderOptions(option.children, true, false)
: null}
</div>
))}
</div>
);
}
renderOuter() {
const {
popOverContainer,
options,
classnames: cx
} = this.props;
const {popOverContainer, options, classnames: cx} = this.props;
let body = (
<RootCloseWrapper
disabled={!this.state.isOpened}
onRootClose={this.close}
>
<div className={cx('NestedSelect-menuOuter')} style={{minWidth: this.target.offsetWidth}} >
<RootCloseWrapper disabled={!this.state.isOpened} onRootClose={this.close}>
<div className={cx('NestedSelect-menuOuter')} style={{minWidth: this.target.offsetWidth}}>
{this.renderOptions(options, false, false)}
</div>
</RootCloseWrapper>
@ -285,7 +263,7 @@ export default class NestedSelectControl extends React.Component<NestedSelectPro
target={() => this.target}
show
>
<PopOver className={cx("NestedSelect-popover")} style={{minWidth: this.target.offsetWidth}}>
<PopOver className={cx('NestedSelect-popover')} style={{minWidth: this.target.offsetWidth}}>
{body}
</PopOver>
</Overlay>
@ -295,20 +273,19 @@ export default class NestedSelectControl extends React.Component<NestedSelectPro
}
render() {
const {
className,
disabled,
placeholder,
selectedOptions,
classnames: cx,
} = this.props;
const {className, disabled, placeholder, selectedOptions, classnames: cx} = this.props;
return (
<div className={cx('NestedSelectControl')} >
<div className={cx('NestedSelect', {
'is-opened': this.state.isOpened,
'is-disabled': disabled
}, className)}
<div className={cx('NestedSelectControl')}>
<div
className={cx(
'NestedSelect',
{
'is-opened': this.state.isOpened,
'is-disabled': disabled
},
className
)}
onClick={this.open}
ref={this.domRef}
>
@ -319,7 +296,7 @@ export default class NestedSelectControl extends React.Component<NestedSelectPro
{this.renderValue()}
{this.renderClear()}
<span className={cx('Select-arrow')}></span>
<span className={cx('Select-arrow')} />
</div>
{this.state.isOpened ? this.renderOuter() : null}
@ -331,4 +308,4 @@ export default class NestedSelectControl extends React.Component<NestedSelectPro
@OptionsControl({
type: 'nested-select'
})
export class NestedSelectControlRenderer extends NestedSelectControl {}
export class NestedSelectControlRenderer extends NestedSelectControl {}

View File

@ -1,8 +1,5 @@
import React from 'react';
import {
FormItem,
FormControlProps,
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import InputNumber from 'rc-input-number';
import {filter} from '../../utils/tpl';
@ -13,7 +10,7 @@ export interface NumberProps extends FormControlProps {
min?: number | string;
step?: number;
precision?: number;
};
}
export default class NumberControl extends React.Component<NumberProps, any> {
static defaultProps: Partial<NumberProps> = {
@ -28,13 +25,9 @@ export default class NumberControl extends React.Component<NumberProps, any> {
}
handleChange(inputValue: any) {
const {
classPrefix: ns,
onChange,
resetValue
} = this.props;
const {classPrefix: ns, onChange, resetValue} = this.props;
onChange(typeof inputValue === 'undefined' ? (resetValue || '') : inputValue);
onChange(typeof inputValue === 'undefined' ? resetValue || '' : inputValue);
}
filterNum(value: number | string | undefined) {
@ -46,17 +39,7 @@ export default class NumberControl extends React.Component<NumberProps, any> {
}
render(): JSX.Element {
const {
className,
classPrefix: ns,
value,
step,
precision,
max,
min,
disabled,
placeholder
} = this.props;
const {className, classPrefix: ns, value, step, precision, max, min, disabled, placeholder} = this.props;
let precisionProps: any = {};
@ -88,5 +71,5 @@ export default class NumberControl extends React.Component<NumberProps, any> {
export class NumberControlRenderer extends NumberControl {
static defaultProps: Partial<FormControlProps> = {
validations: 'isNumeric'
}
};
};
}

View File

@ -1,44 +1,34 @@
import {Api} from '../../types';
import {buildApi, isEffectiveApi, isValidApi, isApiOutdated} from '../../utils/api';
import {
anyChanged
} from '../../utils/helper';
import {anyChanged} from '../../utils/helper';
import {reaction} from 'mobx';
import {
FormControlProps,
registerFormItem,
FormItemBasicConfig
} from './Item';
import {
IFormItemStore
} from '../../store/formItem';
import {FormControlProps, registerFormItem, FormItemBasicConfig} from './Item';
import {IFormItemStore} from '../../store/formItem';
export type OptionsControlComponent = React.ComponentType<FormControlProps>;
import React from 'react';
import { resolveVariableAndFilter } from '../../utils/tpl-builtin';
import { evalExpression } from '../../utils/tpl';
import { Option, OptionProps, normalizeOptions } from '../../components/Select';
import {resolveVariableAndFilter} from '../../utils/tpl-builtin';
import {evalExpression} from '../../utils/tpl';
import {Option, OptionProps, normalizeOptions} from '../../components/Select';
export {
Option
};
export {Option};
export interface OptionsBasicConfig extends FormItemBasicConfig {
autoLoadOptionsFromSource?: boolean;
}
export interface OptionsConfig extends OptionsBasicConfig {
component: React.ComponentType<OptionsControlProps>
component: React.ComponentType<OptionsControlProps>;
}
export interface OptionsControlProps extends FormControlProps, OptionProps {
source?: Api;
name?: string;
onToggle: (option:Option, submitOnChange?: boolean) => void;
onToggle: (option: Option, submitOnChange?: boolean) => void;
onToggleAll: () => void;
selectedOptions: Array<Option>;
setOptions: (value:Array<any>) => void;
setLoading: (value:boolean) => void;
setOptions: (value: Array<any>) => void;
setLoading: (value: boolean) => void;
reloadOptions: () => void;
}
@ -48,7 +38,7 @@ export function registerOptionsControl(config: OptionsConfig) {
// @observer
class FormOptionsItem extends React.Component<FormControlProps, any> {
static displayName = `OptionsControl(${config.type})`;
static defaultProps:Partial<FormControlProps> = {
static defaultProps: Partial<FormControlProps> = {
delimiter: ',',
labelField: 'label',
valueField: 'value',
@ -59,13 +49,13 @@ export function registerOptionsControl(config: OptionsConfig) {
resetValue: '',
...Control.defaultProps
};
static propsList:any = (Control as any).propsList ? [...(Control as any).propsList] : [];
static propsList: any = (Control as any).propsList ? [...(Control as any).propsList] : [];
static ComposedComponent = Control;
reaction:any;
input:any;
constructor(props:FormControlProps) {
reaction: any;
input: any;
constructor(props: FormControlProps) {
super(props);
const formItem = props.formItem as IFormItemStore;
@ -97,14 +87,13 @@ export function registerOptionsControl(config: OptionsConfig) {
} = this.props;
if (formItem) {
this.reaction = reaction(() => JSON.stringify([
formItem.loading,
formItem.selectedOptions,
formItem.filteredOptions
]), () => this.forceUpdate());
this.reaction = reaction(
() => JSON.stringify([formItem.loading, formItem.selectedOptions, formItem.filteredOptions]),
() => this.forceUpdate()
);
}
let loadOptions:boolean = initFetch !== false;
let loadOptions: boolean = initFetch !== false;
if (/^\$(?:([a-z0-9_.]+)|{.+})$/.test(source) && formItem) {
formItem.setOptions(normalizeOptions(resolveVariableAndFilter(source, data, '| raw') || []));
@ -112,7 +101,9 @@ export function registerOptionsControl(config: OptionsConfig) {
}
if (formItem && joinValues === false && defaultValue) {
const selectedOptions = extractValue ? formItem.selectedOptions.map((selectedOption: Option) => selectedOption[valueField || 'value']) : formItem.selectedOptions;
const selectedOptions = extractValue
? formItem.selectedOptions.map((selectedOption: Option) => selectedOption[valueField || 'value'])
: formItem.selectedOptions;
setPrinstineValue(multiple ? selectedOptions.concat() : formItem.selectedOptions[0]);
}
@ -123,43 +114,49 @@ export function registerOptionsControl(config: OptionsConfig) {
this.normalizeValue();
}
shouldComponentUpdate(nextProps:FormControlProps) {
shouldComponentUpdate(nextProps: FormControlProps) {
if (config.strictMode === false || nextProps.strictMode === false) {
return true;
}
if (anyChanged([
'formPristine',
'addOn',
'disabled',
'placeholder',
'required',
'formMode',
'className',
'inputClassName',
'labelClassName',
'label',
'inline',
'options',
'size',
'btnClassName',
'btnActiveClassName',
'buttons',
'columnsCount',
'multiple',
'hideRoot',
'checkAll',
'showIcon',
'showRadio',
'btnDisabled'
], this.props, nextProps)) {
if (
anyChanged(
[
'formPristine',
'addOn',
'disabled',
'placeholder',
'required',
'formMode',
'className',
'inputClassName',
'labelClassName',
'label',
'inline',
'options',
'size',
'btnClassName',
'btnActiveClassName',
'buttons',
'columnsCount',
'multiple',
'hideRoot',
'checkAll',
'showIcon',
'showRadio',
'btnDisabled'
],
this.props,
nextProps
)
) {
return true;
}
return false;
}
componentWillReceiveProps(nextProps:OptionsControlProps) {
componentWillReceiveProps(nextProps: OptionsControlProps) {
const props = this.props;
const formItem = nextProps.formItem as IFormItemStore;
@ -175,7 +172,12 @@ export function registerOptionsControl(config: OptionsConfig) {
if (props.options !== nextProps.options && formItem) {
formItem.setOptions(normalizeOptions(nextProps.options || []));
} else if (config.autoLoadOptionsFromSource !== false && nextProps.source && formItem && (props.source !== nextProps.source || props.data !== nextProps.data)) {
} else if (
config.autoLoadOptionsFromSource !== false &&
nextProps.source &&
formItem &&
(props.source !== nextProps.source || props.data !== nextProps.data)
) {
if (/^\$(?:([a-z0-9_.]+)|{.+})$/.test(nextProps.source as string)) {
const options = resolveVariableAndFilter(props.source, props.data, '| raw');
const nextOptions = resolveVariableAndFilter(nextProps.source as string, nextProps.data, '| raw');
@ -196,33 +198,33 @@ export function registerOptionsControl(config: OptionsConfig) {
}
normalizeValue() {
const {
joinValues,
extractValue,
value,
multiple,
formItem,
valueField
} = this.props;
const {joinValues, extractValue, value, multiple, formItem, valueField} = this.props;
if (
formItem
&& joinValues === false
&& extractValue === false
&& (typeof value === 'string' || typeof value === 'number')
&& formItem.options.length
formItem &&
joinValues === false &&
extractValue === false &&
(typeof value === 'string' || typeof value === 'number') &&
formItem.options.length
) {
formItem.changeValue(multiple ? formItem.selectedOptions.concat() : formItem.selectedOptions[0]);
}
if (
formItem
&& joinValues === false
&& extractValue === true
&& value && !((Array.isArray(value) && value.every(val => typeof val === 'string' || typeof val === 'number')) || typeof value === 'string' || typeof value === 'number')
&& formItem.options.length
formItem &&
joinValues === false &&
extractValue === true &&
value &&
!(
(Array.isArray(value) && value.every(val => typeof val === 'string' || typeof val === 'number')) ||
typeof value === 'string' ||
typeof value === 'number'
) &&
formItem.options.length
) {
const selectedOptions = formItem.selectedOptions.map((selectedOption: Option) => selectedOption[valueField || 'value']);
const selectedOptions = formItem.selectedOptions.map(
(selectedOption: Option) => selectedOption[valueField || 'value']
);
formItem.changeValue(multiple ? selectedOptions.concat() : selectedOptions[0]);
}
}
@ -231,11 +233,11 @@ export function registerOptionsControl(config: OptionsConfig) {
return this.input;
}
inputRef(ref:any) {
inputRef(ref: any) {
this.input = ref;
}
handleToggle(option:Option, submitOnChange?: boolean) {
handleToggle(option: Option, submitOnChange?: boolean) {
const {
onChange,
joinValues,
@ -254,7 +256,7 @@ export function registerOptionsControl(config: OptionsConfig) {
let valueArray = formItem.selectedOptions.concat();
const idx = valueArray.indexOf(option);
let newValue:string|Array<Option>|Option = '';
let newValue: string | Array<Option> | Option = '';
if (multiple) {
if (~idx) {
@ -272,7 +274,7 @@ export function registerOptionsControl(config: OptionsConfig) {
}
} else {
if (~idx && clearable) {
valueArray.splice(idx, 1)
valueArray.splice(idx, 1);
} else {
valueArray = [option];
}
@ -303,10 +305,12 @@ export function registerOptionsControl(config: OptionsConfig) {
return;
}
let valueArray = formItem.selectedOptions.length === formItem.filteredOptions.length ?
[] : formItem.filteredOptions.concat();
let newValue:string|Array<Option>|Option = '';
let valueArray =
formItem.selectedOptions.length === formItem.filteredOptions.length
? []
: formItem.filteredOptions.concat();
let newValue: string | Array<Option> | Option = '';
if (multiple) {
newValue = valueArray;
@ -329,26 +333,20 @@ export function registerOptionsControl(config: OptionsConfig) {
// 当有 action 触发,如果指定了 reload 目标组件,有可能会来到这里面来
reload() {
const {
source,
formItem,
data,
onChange
} = this.props;
const {source, formItem, data, onChange} = this.props;
if (config.autoLoadOptionsFromSource === false || !formItem || !isEffectiveApi(source, data)) {
return;
}
return formItem
.loadOptions(source, data, undefined, false, onChange)
return formItem.loadOptions(source, data, undefined, false, onChange);
}
focus() {
this.input && this.input.focus && this.input.focus();
}
setOptions(options:Array<any>) {
setOptions(options: Array<any>) {
const formItem = this.props.formItem as IFormItemStore;
formItem && formItem.setOptions(normalizeOptions(options || []));
}
@ -358,16 +356,13 @@ export function registerOptionsControl(config: OptionsConfig) {
formItem && formItem.syncOptions();
}
setLoading(value:boolean) {
setLoading(value: boolean) {
const formItem = this.props.formItem as IFormItemStore;
formItem && formItem.setLoading(value);
}
render() {
const {
value,
formItem
} = this.props;
const {value, formItem} = this.props;
return (
<Control
@ -383,7 +378,7 @@ export function registerOptionsControl(config: OptionsConfig) {
syncOptions={this.syncOptions}
reloadOptions={this.reload}
/>
)
);
}
}
@ -394,18 +389,17 @@ export function registerOptionsControl(config: OptionsConfig) {
});
}
export function OptionsControl(config:OptionsBasicConfig) {
return function <T extends React.ComponentType<OptionsControlProps>>(component:T):T {
export function OptionsControl(config: OptionsBasicConfig) {
return function<T extends React.ComponentType<OptionsControlProps>>(component: T): T {
const renderer = registerOptionsControl({
...config,
component: component
});
return renderer.component as any;
}
};
}
export function highlight(text:string, input?:string, hlClassName:string = 'is-matched') {
export function highlight(text: string, input?: string, hlClassName: string = 'is-matched') {
if (!input) {
return text;
}
@ -417,14 +411,18 @@ export function highlight(text:string, input?:string, hlClassName:string = 'is-m
}
const parts = text.split(reg);
const dom:Array<any> = [];
const dom: Array<any> = [];
parts.forEach((text:string, index) => {
parts.forEach((text: string, index) => {
text && dom.push(<span key={index}>{text}</span>);
dom.push(<span className={hlClassName} key={`${index}-hl`}>{input}</span>);
dom.push(
<span className={hlClassName} key={`${index}-hl`}>
{input}
</span>
);
});
dom.pop();
return dom;
}
}

View File

@ -1,19 +1,16 @@
import React from 'react';
import {
Renderer,
RendererProps
} from '../../factory';
import {Renderer, RendererProps} from '../../factory';
import Panel from '../Panel';
import { Schema } from '../../types';
import {Schema} from '../../types';
import cx from 'classnames';
@Renderer({
test: /(^|\/)form(?:\/.+)?\/control\/(?:\d+\/)?panel$/,
weight: -100,
name: "panel-control"
name: 'panel-control'
})
export class PanelRenderer extends Panel {
renderBody():JSX.Element | null {
renderBody(): JSX.Element | null {
const {
render,
renderFormItems,
@ -26,21 +23,25 @@ export class PanelRenderer extends Panel {
formMode,
horizontal,
$path,
classPrefix: ns,
classPrefix: ns
} = this.props;
if (!body && (controls || tabs || fieldSet)) {
let props:any = {};
let props: any = {};
mode && (props.mode = mode);
horizontal && (props.horizontal = horizontal);
return (
<div className={cx(`${ns}Form--${props.mode || formMode || 'normal'}`, bodyClassName)}>
{renderFormItems({
controls,
tabs,
fieldSet
}, ($path as string).replace(/^.*form\//, ''), props)}
{renderFormItems(
{
controls,
tabs,
fieldSet
},
($path as string).replace(/^.*form\//, ''),
props
)}
</div>
);
}

View File

@ -1,22 +1,14 @@
import React from 'react';
import {
OptionsControl,
OptionsControlProps,
Option
} from './Options';
import {OptionsControl, OptionsControlProps, Option} from './Options';
import cx from 'classnames';
import Button from '../../components/Button';
import {
SchemaNode,
Schema,
Action
} from '../../types';
import {SchemaNode, Schema, Action} from '../../types';
import find = require('lodash/find');
import {anyChanged, autobind, getVariable, noop} from '../../utils/helper';
import findIndex = require('lodash/findIndex');
import Html from '../../components/Html';
import { filter } from '../../utils/tpl';
import { Icon } from '../../components/icons';
import {filter} from '../../utils/tpl';
import {Icon} from '../../components/icons';
import {isEmpty} from '../../utils/helper';
import {dataMapping} from '../../utils/tpl-builtin';
@ -24,32 +16,32 @@ export interface PickerProps extends OptionsControlProps {
modalMode: 'dialog' | 'drawer';
pickerSchema: object;
labelField: string;
};
}
export interface PickerState {
isOpened: boolean;
isFocused: boolean;
schema: SchemaNode;
};
}
export default class PickerControl extends React.PureComponent<PickerProps, any> {
static propsList: Array<string> = [
"modalMode",
"pickerSchema",
"labelField",
"onChange",
"options",
"value",
"inline",
"multiple",
"embed",
"resetValue",
"placeholder"
'modalMode',
'pickerSchema',
'labelField',
'onChange',
'options',
'value',
'inline',
'multiple',
'embed',
'resetValue',
'placeholder'
];
static defaultProps: Partial<PickerProps> = {
modalMode: 'dialog',
multiple: false,
placeholder: "请点击按钮选择",
placeholder: '请点击按钮选择',
pickerSchema: {
mode: 'list',
listItem: {
@ -57,7 +49,7 @@ export default class PickerControl extends React.PureComponent<PickerProps, any>
}
},
embed: false
}
};
state: PickerState = {
isOpened: false,
@ -70,11 +62,7 @@ export default class PickerControl extends React.PureComponent<PickerProps, any>
componentWillReceiveProps(nextProps: PickerProps) {
const props = this.props;
if (anyChanged([
'pickerSchema',
'multiple',
'source'
], props, nextProps)) {
if (anyChanged(['pickerSchema', 'multiple', 'source'], props, nextProps)) {
this.setState({
schema: this.buildSchema(nextProps)
});
@ -94,8 +82,8 @@ export default class PickerControl extends React.PureComponent<PickerProps, any>
checkOnItemClick: true,
// 不支持批量操作,会乱套
bulkActions: props.multiple ? (props.pickerSchema as Schema).bulkActions : [],
}
bulkActions: props.multiple ? (props.pickerSchema as Schema).bulkActions : []
};
}
reload() {
@ -119,7 +107,7 @@ export default class PickerControl extends React.PureComponent<PickerProps, any>
@autobind
handleModalConfirm(values: Array<any>, action: Action, ctx: any, components: Array<any>) {
const idx = findIndex(components, (item: any) => item.props.type === "crud");
const idx = findIndex(components, (item: any) => item.props.type === 'crud');
this.handleChange(values[idx].items);
this.close();
}
@ -144,7 +132,9 @@ export default class PickerControl extends React.PureComponent<PickerProps, any>
if (joinValues) {
value = items.map((item: any) => item[valueField || 'value']).join(delimiter || ',');
} else if (extractValue) {
value = multiple ? items.map((item: any) => item[valueField || 'value']) : (items[0] && items[0][valueField || 'value'] || '');
value = multiple
? items.map((item: any) => item[valueField || 'value'])
: (items[0] && items[0][valueField || 'value']) || '';
} else {
value = multiple ? items : items[0];
}
@ -163,15 +153,7 @@ export default class PickerControl extends React.PureComponent<PickerProps, any>
}
removeItem(index: number) {
const {
selectedOptions,
joinValues,
extractValue,
delimiter,
valueField,
onChange,
multiple
} = this.props;
const {selectedOptions, joinValues, extractValue, delimiter, valueField, onChange, multiple} = this.props;
const items = selectedOptions.concat();
items.splice(index, 1);
@ -180,7 +162,9 @@ export default class PickerControl extends React.PureComponent<PickerProps, any>
if (joinValues) {
value = items.map((item: any) => item[valueField || 'value']).join(delimiter || ',');
} else if (extractValue) {
value = multiple ? items.map((item: any) => item[valueField || 'value']) : (items[0] && items[0][valueField || 'value'] || '');
value = multiple
? items.map((item: any) => item[valueField || 'value'])
: (items[0] && items[0][valueField || 'value']) || '';
} else {
value = multiple ? items : items[0];
}
@ -189,12 +173,12 @@ export default class PickerControl extends React.PureComponent<PickerProps, any>
}
@autobind
handleKeyDown(e:React.KeyboardEvent) {
handleKeyDown(e: React.KeyboardEvent) {
const selectedOptions = this.props.selectedOptions;
if (e.key === ' ') {
this.open();
} else if (selectedOptions.length && e.key == "Backspace") {
} else if (selectedOptions.length && e.key == 'Backspace') {
this.removeItem(selectedOptions.length - 1);
}
}
@ -205,7 +189,7 @@ export default class PickerControl extends React.PureComponent<PickerProps, any>
isFocused: true
});
}
@autobind
handleBlur() {
this.setState({
@ -220,33 +204,36 @@ export default class PickerControl extends React.PureComponent<PickerProps, any>
@autobind
clearValue() {
const {
onChange,
resetValue
} = this.props;
const {onChange, resetValue} = this.props;
onChange(resetValue !== void 0 ? resetValue : '')
onChange(resetValue !== void 0 ? resetValue : '');
}
renderValues() {
const {
classPrefix: ns,
selectedOptions,
labelField,
labelTpl,
disabled
} = this.props;
const {classPrefix: ns, selectedOptions, labelField, labelTpl, disabled} = this.props;
return (
<div className={`${ns}Picker-values`}>
{selectedOptions.map((item, index) => (
<div key={index} className={cx(`${ns}Picker-value`, {
'is-disabled': disabled
})}>
<span data-tooltip="删除" data-position="bottom" className={`${ns}Picker-valueIcon`} onClick={this.removeItem.bind(this, index)}>×</span>
<div
key={index}
className={cx(`${ns}Picker-value`, {
'is-disabled': disabled
})}
>
<span
data-tooltip="删除"
data-position="bottom"
className={`${ns}Picker-valueIcon`}
onClick={this.removeItem.bind(this, index)}
>
×
</span>
<span className={`${ns}Picker-valueLabel`}>
{labelTpl
? (<Html html={filter(labelTpl, item)} />)
: getVariable(item, labelField || 'label') || getVariable(item, 'id')}
{labelTpl ? (
<Html html={filter(labelTpl, item)} />
) : (
getVariable(item, labelField || 'label') || getVariable(item, 'id')
)}
</span>
</div>
))}
@ -256,14 +243,7 @@ export default class PickerControl extends React.PureComponent<PickerProps, any>
@autobind
renderBody() {
const {
render,
selectedOptions,
options,
multiple,
valueField,
embed
} = this.props;
const {render, selectedOptions, options, multiple, valueField, embed} = this.props;
return render('modal-body', this.state.schema, {
value: selectedOptions,
@ -294,9 +274,7 @@ export default class PickerControl extends React.PureComponent<PickerProps, any>
return (
<div className={cx(`PickerControl`, className)}>
{embed ? (
<div className={cx('Picker')}>
{this.renderBody()}
</div>
<div className={cx('Picker')}>{this.renderBody()}</div>
) : (
<div
className={cx(`Picker`, {
@ -313,7 +291,7 @@ export default class PickerControl extends React.PureComponent<PickerProps, any>
<div className={cx('Picker-valueWrap')}>
{this.renderValues()}
<input
onChange={noop}
value={''}
@ -324,70 +302,75 @@ export default class PickerControl extends React.PureComponent<PickerProps, any>
/>
</div>
{clearable && !disabled && selectedOptions.length ? (<a onClick={this.clearValue} className={cx('Picker-clear')}><Icon icon="close" className="icon" /></a>) : null}
{clearable && !disabled && selectedOptions.length ? (
<a onClick={this.clearValue} className={cx('Picker-clear')}>
<Icon icon="close" className="icon" />
</a>
) : null}
<span onClick={this.open} className={cx('Picker-btn')}></span>
<span onClick={this.open} className={cx('Picker-btn')} />
</div>
{render('modal', {
title: '请选择',
size: size,
type: modalMode,
body: {
children: this.renderBody
{render(
'modal',
{
title: '请选择',
size: size,
type: modalMode,
body: {
children: this.renderBody
}
},
{
key: 'modal',
lazyRender: !!source,
onConfirm: this.handleModalConfirm,
onClose: this.close,
show: this.state.isOpened
}
}, {
key: 'modal',
lazyRender: !!source,
onConfirm: this.handleModalConfirm,
onClose: this.close,
show: this.state.isOpened
})}
)}
</div>
// <div className={`${ns}Picker`}>
// {this.renderValues()}
// <div className={`${ns}Picker`}>
// {this.renderValues()}
// <Button
// classPrefix={ns}
// className={`${ns}Picker-pickBtn`}
// tooltip="点击选择"
// tooltipContainer={env && env.getModalContainer ? env.getModalContainer() : undefined}
// level="info"
// size="sm"
// disabled={disabled}
// onClick={this.open}
// iconOnly
// >
// 选定
// </Button>
// <Button
// classPrefix={ns}
// className={`${ns}Picker-pickBtn`}
// tooltip="点击选择"
// tooltipContainer={env && env.getModalContainer ? env.getModalContainer() : undefined}
// level="info"
// size="sm"
// disabled={disabled}
// onClick={this.open}
// iconOnly
// >
// 选定
// </Button>
// {render('modal', {
// title: '请选择',
// size: size,
// type: modalMode,
// body: {
// children: this.renderBody
// }
// }, {
// key: 'modal',
// lazyRender: !!source,
// onConfirm: this.handleModalConfirm,
// onClose: this.close,
// show: this.state.isOpened
// })}
// </div>
)}
// {render('modal', {
// title: '请选择',
// size: size,
// type: modalMode,
// body: {
// children: this.renderBody
// }
// }, {
// key: 'modal',
// lazyRender: !!source,
// onConfirm: this.handleModalConfirm,
// onClose: this.close,
// show: this.state.isOpened
// })}
// </div>
)}
</div>
);
}
}
@OptionsControl({
type: 'picker',
autoLoadOptionsFromSource: false,
sizeMutable: false
})
export class PickerControlRenderer extends PickerControl {};
export class PickerControlRenderer extends PickerControl {}

View File

@ -1,38 +1,24 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import Radios from '../../components/Radios';
import {
OptionsControl,
OptionsControlProps,
Option
} from './Options';
import {OptionsControl, OptionsControlProps, Option} from './Options';
import {autobind, isEmpty} from '../../utils/helper';
import {dataMapping} from '../../utils/tpl-builtin';
export interface RadiosProps extends OptionsControlProps {
placeholder?: any;
columnsCount?: number;
};
}
export default class RadiosControl extends React.Component<RadiosProps, any> {
static defaultProps:Partial<RadiosProps> = {
static defaultProps: Partial<RadiosProps> = {
columnsCount: 1
}
};
@autobind
handleChange(option: Option) {
const {
joinValues,
extractValue,
valueField,
onChange,
autoFill,
onBulkChange
} = this.props;
const {joinValues, extractValue, valueField, onChange, autoFill, onBulkChange} = this.props;
const sendTo = autoFill && !isEmpty(autoFill) && dataMapping(autoFill, option);
sendTo && onBulkChange && onBulkChange(sendTo);
@ -44,12 +30,11 @@ export default class RadiosControl extends React.Component<RadiosProps, any> {
onChange && onChange(option);
}
reload() {
const reload = this.props.reloadOptions;
reload && reload();
}
render() {
const {
className,
@ -73,7 +58,7 @@ export default class RadiosControl extends React.Component<RadiosProps, any> {
<Radios
inline={inline || formMode === 'inline'}
className={cx(`${ns}RadiosControl`, className)}
value={(typeof value === 'undefined' || value === null) ? '' : value}
value={typeof value === 'undefined' || value === null ? '' : value}
disabled={disabled}
onChange={this.handleChange}
joinValues={joinValues}
@ -97,5 +82,4 @@ export class RadiosControlRenderer extends RadiosControl {
static defaultProps = {
multiple: false
};
};
}

View File

@ -2,13 +2,10 @@ import React from 'react';
import isNumber = require('lodash/isNumber');
import isObject = require('lodash/isObject');
import isEqual = require('lodash/isEqual');
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import InputRange from '../../components/Range';
import { Icon } from '../../components/icons';
import {Icon} from '../../components/icons';
export interface RangeProps extends FormControlProps {
max?: number;
@ -25,7 +22,7 @@ export interface RangeProps extends FormControlProps {
multiple?: boolean;
joinValues?: boolean;
delimiter?: string;
};
}
export interface DefaultProps {
max: number;
@ -38,21 +35,21 @@ export interface DefaultProps {
multiple: boolean;
joinValues: boolean;
delimiter: string;
};
}
export function formatValue(value: string | number | {min: number, max: number}, props: Partial<RangeProps>) {
export function formatValue(value: string | number | {min: number; max: number}, props: Partial<RangeProps>) {
if (props.multiple) {
if (typeof value === 'string') {
const [minValue, maxValue] = value.split(props.delimiter || ',').map(v => Number(v));
return {
min: props.min && minValue < props.min && props.min || minValue || props.min,
max: props.max && maxValue > props.max && props.max || maxValue || props.max
}
min: (props.min && minValue < props.min && props.min) || minValue || props.min,
max: (props.max && maxValue > props.max && props.max) || maxValue || props.max
};
} else if (typeof value === 'object') {
return {
min: props.min && value.min < props.min && props.min || value.min || props.min,
max: props.max && value.max > props.max && props.max || value.max || props.max
}
min: (props.min && value.min < props.min && props.min) || value.min || props.min,
max: (props.max && value.max > props.max && props.max) || value.max || props.max
};
}
}
return value || props.min;
@ -61,13 +58,17 @@ export function formatValue(value: string | number | {min: number, max: number},
type PropsWithDefaults = RangeProps & DefaultProps;
export interface RangeState {
value: {
min?: number,
max?: number
} | number | string | undefined;
value:
| {
min?: number;
max?: number;
}
| number
| string
| undefined;
minValue?: any;
maxValue?: any;
};
maxValue?: any;
}
export default class RangeControl extends React.PureComponent<RangeProps, RangeState> {
midLabel?: HTMLSpanElement;
@ -83,7 +84,7 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
multiple: false,
joinValues: true,
delimiter: ','
}
};
constructor(props: RangeProps) {
super(props);
@ -141,10 +142,7 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
}
updateStyle() {
const {
showInput,
classPrefix: ns
} = this.props;
const {showInput, classPrefix: ns} = this.props;
let offsetWidth = (this.midLabel as HTMLSpanElement).offsetWidth;
let left = `calc(50% - ${offsetWidth / 2}px)`;
@ -191,54 +189,52 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
if (multiple && joinValues) {
endValue = [value.min, value.max].join(delimiter || ',');
}
const {
onChange
} = this.props;
this.setState({
value
}, () => onChange(endValue));
const {onChange} = this.props;
this.setState(
{
value
},
() => onChange(endValue)
);
}
getStepPrecision() {
const {
step
} = this.props;
const {step} = this.props;
return typeof step !== 'number' || step >= 1 || step < 0
? 0
: step.toString().split(".")[1].length;
return typeof step !== 'number' || step >= 1 || step < 0 ? 0 : step.toString().split('.')[1].length;
}
getValue(value: any, type?: string) {
const {
max,
min,
step
} = this.props as PropsWithDefaults;
const {max, min, step} = this.props as PropsWithDefaults;
const {value: stateValue} = this.state;
if (value === '' || value === '-' || new RegExp('^[-]?\\d+[.]{1}[0]{0,' + this.getStepPrecision() + '}$').test(value)) {
if (
value === '' ||
value === '-' ||
new RegExp('^[-]?\\d+[.]{1}[0]{0,' + this.getStepPrecision() + '}$').test(value)
) {
return value;
}
value = Math.round(parseFloat(value) / step) * step;
value = (step < 1) ? parseFloat(value.toFixed(this.getStepPrecision())) : ~~value;
value = step < 1 ? parseFloat(value.toFixed(this.getStepPrecision())) : ~~value;
switch(type) {
case 'min':
{
if (isObject(stateValue) && isNumber(stateValue.max)) {
if (value >= stateValue.max && (min <= stateValue.max - step)) {
return stateValue.max - step;
}
if (value < stateValue.max - step) {
return value;
}
switch (type) {
case 'min': {
if (isObject(stateValue) && isNumber(stateValue.max)) {
if (value >= stateValue.max && min <= stateValue.max - step) {
return stateValue.max - step;
}
if (value < stateValue.max - step) {
return value;
}
return min;
}
return min;
}
case 'max':
return isObject(stateValue) && isNumber(stateValue.min) ? (value > max && max) || (value <= stateValue.min && (stateValue.min + step)) || value : max;
return isObject(stateValue) && isNumber(stateValue.min)
? (value > max && max) || (value <= stateValue.min && stateValue.min + step) || value
: max;
default:
return (value < min && min) || (value > max && max) || value;
}
@ -253,28 +249,28 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
handleMinInputBlur(evt: React.ChangeEvent<HTMLInputElement>) {
const minValue = this.getValue(evt.target.value, 'min');
const {value} = this.state;
isObject(value) ?
this.setState({
value: {
min: minValue,
max: value.max
},
minValue: minValue
})
isObject(value)
? this.setState({
value: {
min: minValue,
max: value.max
},
minValue: minValue
})
: null;
}
handleMaxInputBlur(evt: React.ChangeEvent<HTMLInputElement>) {
const maxValue = this.getValue(evt.target.value, 'max');
const {value} = this.state;
isObject(value) ?
this.setState({
value: {
min: value.min,
max: maxValue
},
maxValue: maxValue
})
isObject(value)
? this.setState({
value: {
min: value.min,
max: maxValue
},
maxValue: maxValue
})
: null;
}
@ -303,15 +299,21 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
showInput,
multiple,
classnames: cx,
classPrefix: ns,
classPrefix: ns
} = this.props as PropsWithDefaults;
return (
<div className={cx("RangeControl", {
'RangeControl--withInput': showInput,
'RangeControl--clearable': clearable,
'is-multiple': multiple
}, className)}>
<div
className={cx(
'RangeControl',
{
'RangeControl--withInput': showInput,
'RangeControl--clearable': clearable,
'is-multiple': multiple
},
className
)}
>
<InputRange
classPrefix={ns}
value={this.state.value}
@ -325,15 +327,15 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
multiple={multiple}
/>
<span className={cx("InputRange-label InputRange-label--mid")} ref={this.midLabelRef}>
<span className={cx("InputRange-labelContainer")}>
<span className={cx('InputRange-label InputRange-label--mid')} ref={this.midLabelRef}>
<span className={cx('InputRange-labelContainer')}>
{((max + min) / 2).toFixed(this.getStepPrecision()) + unit}
</span>
</span>
{showInput ?
{showInput ? (
multiple && isObject(this.state.value) ? (
<div className={cx("InputRange-input is-multiple")}>
<div className={cx('InputRange-input is-multiple')}>
<input
className={this.state.value.min !== min ? 'is-active' : ''}
type="text"
@ -343,7 +345,7 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
onChange={this.handleMinInputChange}
onBlur={this.handleMinInputBlur}
/>
<span className={cx("InputRange-input-separator")}> - </span>
<span className={cx('InputRange-input-separator')}> - </span>
<input
className={this.state.value.max !== max ? 'is-active' : ''}
type="text"
@ -355,7 +357,7 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
/>
</div>
) : (
<div className={cx("InputRange-input")}>
<div className={cx('InputRange-input')}>
<input
className={this.state.value !== min ? 'is-active' : ''}
type="text"
@ -366,14 +368,21 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
/>
</div>
)
: null}
) : null}
{clearable && !disabled && showInput ? (
<a onClick={() => this.clearValue()} className={cx("InputRange-clear", {
'is-active': (multiple ? isEqual(this.state.value, {min: min, max: max}) : this.state.value !== min)
})}><Icon icon="close" className="icon" /></a>
<a
onClick={() => this.clearValue()}
className={cx('InputRange-clear', {
'is-active': multiple
? isEqual(this.state.value, {min: min, max: max})
: this.state.value !== min
})}
>
<Icon icon="close" className="icon" />
</a>
) : null}
</div >
</div>
);
}
}
@ -382,4 +391,4 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
test: /(^|\/)form(?:\/.+)?\/control\/(?:\d+\/)?(slider|range)$/,
name: 'range-control'
})
export class RangeControlRenderer extends RangeControl { };
export class RangeControlRenderer extends RangeControl {}

View File

@ -1,8 +1,5 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import Rating from '../../components/Rating';
export interface RatingProps extends FormControlProps {
@ -10,27 +7,18 @@ export interface RatingProps extends FormControlProps {
count: number;
half: boolean;
readOnly: boolean;
};
}
export default class RatingControl extends React.Component<RatingProps, any> {
static defaultProps:Partial<RatingProps> = {
static defaultProps: Partial<RatingProps> = {
value: 0,
count: 5,
half: false,
readOnly: false
}
};
render() {
const {
className,
value,
count,
half,
readOnly,
onChange,
size,
classnames: cx,
} = this.props;
const {className, value, count, half, readOnly, onChange, size, classnames: cx} = this.props;
return (
<div className={cx('RatingControl', className)}>
@ -41,9 +29,8 @@ export default class RatingControl extends React.Component<RatingProps, any> {
half={half}
readOnly={readOnly}
size={size}
onChange={(value:any) => onChange(value)}
>
</Rating>
onChange={(value: any) => onChange(value)}
/>
</div>
);
}
@ -53,5 +40,4 @@ export default class RatingControl extends React.Component<RatingProps, any> {
type: 'rating',
sizeMutable: false
})
export class RatingControlRenderer extends RatingControl {};
export class RatingControlRenderer extends RatingControl {}

View File

@ -1,57 +1,53 @@
/**
* @file filter
* @author fex
*/
* @file filter
* @author fex
*/
/* eslint fecs-indent: [0, "space", 2, 2] */
import React from 'react';
import cx from 'classnames';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
const LANG:{
[propName:string]: string;
const LANG: {
[propName: string]: string;
} = {
'secondly': '秒',
'minutely': '分',
'hourly': '时',
'daily': '天',
'weekdays': '周中',
'weekly': '周',
'monthly': '月',
'yearly': '年'
secondly: '秒',
minutely: '分',
hourly: '时',
daily: '天',
weekdays: '周中',
weekly: '周',
monthly: '月',
yearly: '年'
};
import Select from '../../components/Select';
import InputRange from '../../components/Range';
import { Option } from './Options';
import {Option} from './Options';
export interface RepeatProps extends FormControlProps {
options?: string;
placeholder?:string;
};
placeholder?: string;
}
export default class RepeatControl extends React.Component<RepeatProps, any> {
static defaultProps = {
// options: 'secondly,minutely,hourly,daily,weekdays,weekly,monthly,yearly'
options: 'hourly,daily,weekly,monthly',
placeholder: '不重复'
};
constructor(props:RepeatProps) {
constructor(props: RepeatProps) {
super(props);
this.handleOptionChange = this.handleOptionChange.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleOptionChange(option:Option) {
handleOptionChange(option: Option) {
this.props.onChange(option.value);
}
handleChange(value:string) {
handleChange(value: string) {
const option = this.props.value;
const parts = option ? option.split(':') : [];
@ -61,14 +57,9 @@ export default class RepeatControl extends React.Component<RepeatProps, any> {
renderInput() {
const value = this.props.value;
const parts = value ? value.split(':') : [];
let {
options,
placeholder,
disabled,
classPrefix: ns
} = this.props;
let {options, placeholder, disabled, classPrefix: ns} = this.props;
let optionsArray:Array<Option> = [];
let optionsArray: Array<Option> = [];
optionsArray = (options as string).split(',').map(key => ({
label: LANG[key] || '不支持',
@ -85,119 +76,115 @@ export default class RepeatControl extends React.Component<RepeatProps, any> {
parts[1] = parseInt(parts[1], 10) || 1;
switch (parts[0]) {
case 'secondly':
input = (
<InputRange
key="input"
classPrefix={ns}
value={parts[1]}
min={1}
step={5}
max={60}
disabled={disabled}
onChange={(value:any) => this.handleChange(value)}
/>
);
break;
input = (
<InputRange
key="input"
classPrefix={ns}
value={parts[1]}
min={1}
step={5}
max={60}
disabled={disabled}
onChange={(value: any) => this.handleChange(value)}
/>
);
break;
case 'minutely':
input = (
<InputRange
key="input"
classPrefix={ns}
value={parts[1]}
min={1}
step={5}
max={60}
disabled={disabled}
onChange={(value:any) => this.handleChange(value)}
/>
);
break;
input = (
<InputRange
key="input"
classPrefix={ns}
value={parts[1]}
min={1}
step={5}
max={60}
disabled={disabled}
onChange={(value: any) => this.handleChange(value)}
/>
);
break;
case 'hourly':
input = (
<InputRange
key="input"
classPrefix={ns}
value={parts[1]}
min={1}
step={1}
max={24}
disabled={disabled}
onChange={(value:any) => this.handleChange(value)}
/>
);
break;
input = (
<InputRange
key="input"
classPrefix={ns}
value={parts[1]}
min={1}
step={1}
max={24}
disabled={disabled}
onChange={(value: any) => this.handleChange(value)}
/>
);
break;
case 'daily':
input = (
<InputRange
key="input"
classPrefix={ns}
value={parts[1]}
min={1}
step={1}
max={30}
disabled={disabled}
onChange={(value:any) => this.handleChange(value)}
/>
);
break;
input = (
<InputRange
key="input"
classPrefix={ns}
value={parts[1]}
min={1}
step={1}
max={30}
disabled={disabled}
onChange={(value: any) => this.handleChange(value)}
/>
);
break;
case 'weekly':
input = (
<InputRange
key="input"
classPrefix={ns}
value={parts[1]}
min={1}
step={1}
max={12}
disabled={disabled}
onChange={(value:any) => this.handleChange(value)}
/>
);
break;
input = (
<InputRange
key="input"
classPrefix={ns}
value={parts[1]}
min={1}
step={1}
max={12}
disabled={disabled}
onChange={(value: any) => this.handleChange(value)}
/>
);
break;
case 'monthly':
input = (
<InputRange
key="input"
classPrefix={ns}
value={parts[1]}
min={1}
step={1}
max={12}
disabled={disabled}
onChange={(value:any) => this.handleChange(value)}
/>
);
break;
input = (
<InputRange
key="input"
classPrefix={ns}
value={parts[1]}
min={1}
step={1}
max={12}
disabled={disabled}
onChange={(value: any) => this.handleChange(value)}
/>
);
break;
case 'yearly':
input = (
<InputRange
classPrefix={ns}
key="input"
className="v-middle"
value={parts[1]}
min={1}
step={1}
max={20}
disabled={disabled}
onChange={(value:any) => this.handleChange(value)}
/>
);
break;
input = (
<InputRange
classPrefix={ns}
key="input"
className="v-middle"
value={parts[1]}
min={1}
step={1}
max={20}
disabled={disabled}
onChange={(value: any) => this.handleChange(value)}
/>
);
break;
}
return (
<div className="repeat-control hbox">
{input ? (
<div className="col v-middle" style={{width: 30}}>
<span></span>
<span></span>
</div>
) : null}
{input ? (
<div className="col v-middle">
{input}
</div>
) : null}
{input ? <div className="col v-middle">{input}</div> : null}
<div className="col v-middle repeat-btn">
<Select
@ -218,22 +205,14 @@ export default class RepeatControl extends React.Component<RepeatProps, any> {
}
render() {
const {
className,
classPrefix: ns
} = this.props;
const {className, classPrefix: ns} = this.props;
return (
<div className={cx(`${ns}RepeatControl`, className)}>
{this.renderInput()}
</div>
);
return <div className={cx(`${ns}RepeatControl`, className)}>{this.renderInput()}</div>;
}
}
@FormItem({
type: 'repeat',
sizeMutable: false
})
export class RepeatControlRenderer extends RepeatControl {};
export class RepeatControlRenderer extends RepeatControl {}

View File

@ -1,44 +1,103 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import LazyComponent from '../../components/LazyComponent';
import { noop } from '../../utils/helper';
import {noop} from '../../utils/helper';
export interface RichTextProps extends FormControlProps {
options?: any;
};
}
function loadComponent():Promise<React.ReactType> {
return new Promise((resolve) => (require as any)(['../../components/RichText'], (component:any) => resolve(component.default)));
function loadComponent(): Promise<React.ReactType> {
return new Promise(resolve =>
(require as any)(['../../components/RichText'], (component: any) => resolve(component.default))
);
}
export default class RichTextControl extends React.Component<RichTextProps, any> {
static defaultProps:Partial<RichTextProps> = {
static defaultProps: Partial<RichTextProps> = {
reciever: '/api/upload/image',
videoReciever: '/api/upload/video',
placeholder: '请输入',
options: {
language: 'zh_cn',
toolbarButtonsSM: [
'paragraphFormat', 'quote', 'color', '|', 'bold', 'italic', 'underline', 'strikeThrough', '|', 'formatOL', 'formatUL', 'align', '|', 'insertLink', 'insertImage', 'insertEmotion', 'insertTable', '|', 'undo', 'redo', 'html'
'paragraphFormat',
'quote',
'color',
'|',
'bold',
'italic',
'underline',
'strikeThrough',
'|',
'formatOL',
'formatUL',
'align',
'|',
'insertLink',
'insertImage',
'insertEmotion',
'insertTable',
'|',
'undo',
'redo',
'html'
],
toolbarButtonsMD: [
'paragraphFormat', 'quote', 'color', '|', 'bold', 'italic', 'underline', 'strikeThrough', '|', 'formatOL', 'formatUL', 'align', '|', 'insertLink', 'insertImage', 'insertEmotion', 'insertTable', '|', 'undo', 'redo', 'html'
'paragraphFormat',
'quote',
'color',
'|',
'bold',
'italic',
'underline',
'strikeThrough',
'|',
'formatOL',
'formatUL',
'align',
'|',
'insertLink',
'insertImage',
'insertEmotion',
'insertTable',
'|',
'undo',
'redo',
'html'
],
toolbarButtons: [
'paragraphFormat', 'quote', 'color', '|', 'bold', 'italic', 'underline', 'strikeThrough', '|', 'formatOL', 'formatUL', 'align', '|', 'insertLink', 'insertImage', 'insertEmotion', 'insertTable', '|', 'undo', 'redo', 'html'
'paragraphFormat',
'quote',
'color',
'|',
'bold',
'italic',
'underline',
'strikeThrough',
'|',
'formatOL',
'formatUL',
'align',
'|',
'insertLink',
'insertImage',
'insertEmotion',
'insertTable',
'|',
'undo',
'redo',
'html'
]
}
}
};
state = {
focused: false
};
config:any = null;
constructor(props:RichTextProps) {
config: any = null;
constructor(props: RichTextProps) {
super(props);
this.handleFocus = this.handleFocus.bind(this);
@ -47,7 +106,23 @@ export default class RichTextControl extends React.Component<RichTextProps, any>
this.config = {
imageAllowedTypes: ['jpeg', 'jpg', 'png', 'gif'],
imageDefaultAlign: 'left',
imageEditButtons: props.imageEditable ? ["imageReplace", "imageAlign", "imageRemove", "|", "imageLink", "linkOpen", "linkEdit", "linkRemove", "-", "imageDisplay", "imageStyle", "imageAlt", "imageSize"] : [],
imageEditButtons: props.imageEditable
? [
'imageReplace',
'imageAlign',
'imageRemove',
'|',
'imageLink',
'linkOpen',
'linkEdit',
'linkRemove',
'-',
'imageDisplay',
'imageStyle',
'imageAlt',
'imageSize'
]
: [],
key: props.env.richTextToken,
...props.options,
editorClass: props.editorClass,
@ -61,7 +136,7 @@ export default class RichTextControl extends React.Component<RichTextProps, any>
from: 'rich-text'
},
events: {
...props.options && props.options.events,
...(props.options && props.options.events),
'froalaEditor.focus': this.handleFocus,
'froalaEditor.blur': this.handleBlur
}
@ -88,17 +163,10 @@ export default class RichTextControl extends React.Component<RichTextProps, any>
}
render() {
const {
className,
classPrefix: ns,
value,
onChange,
disabled,
size
} = this.props;
const {className, classPrefix: ns, value, onChange, disabled, size} = this.props;
return (
<div
<div
className={cx(`${ns}RichTextControl`, className, {
'is-focused': this.state.focused,
'is-disabled': disabled
@ -118,10 +186,8 @@ export default class RichTextControl extends React.Component<RichTextProps, any>
}
}
@FormItem({
type: 'rich-text',
sizeMutable: false
})
export class RichTextControlRenderer extends RichTextControl {};
export class RichTextControlRenderer extends RichTextControl {}

View File

@ -1,10 +1,6 @@
import React from 'react';
import cx from 'classnames';
import {
OptionsControl,
OptionsControlProps,
Option
} from './Options';
import {OptionsControl, OptionsControlProps, Option} from './Options';
import Select from '../../components/Select';
import find = require('lodash/find');
import debouce = require('lodash/debounce');
@ -16,17 +12,17 @@ import {dataMapping} from '../../utils/tpl-builtin';
export interface SelectProps extends OptionsControlProps {
autoComplete?: Api;
searchable?: boolean;
};
}
export default class SelectControl extends React.Component<SelectProps, any> {
static defaultProps: Partial<SelectProps> = {
clearable: false,
searchable: false
}
};
input: any;
cache: {
[propName: string]: any
[propName: string]: any;
} = {};
constructor(props: SelectProps) {
super(props);
@ -72,13 +68,21 @@ export default class SelectControl extends React.Component<SelectProps, any> {
if (joinValues) {
if (multiple) {
newValue = Array.isArray(value) ? value.map(item => item.value).join(delimiter) as string : value ? (value as Option).value : '';
newValue = Array.isArray(value)
? (value.map(item => item.value).join(delimiter) as string)
: value
? (value as Option).value
: '';
} else {
newValue = newValue ? (newValue as Option).value : '';
}
} else if (extractValue) {
if (multiple) {
newValue = Array.isArray(value) ? value.map(item => item.value) : value ? [(value as Option).value] : [''];
newValue = Array.isArray(value)
? value.map(item => item.value)
: value
? [(value as Option).value]
: [''];
} else {
newValue = newValue ? (newValue as Option).value : '';
}
@ -93,32 +97,25 @@ export default class SelectControl extends React.Component<SelectProps, any> {
}
loadRemote(input: string) {
const {
autoComplete,
env,
data,
setOptions,
setLoading,
} = this.props;
const {autoComplete, env, data, setOptions, setLoading} = this.props;
if (!env || !env.fetcher) {
throw new Error('fetcher is required');
}
if (this.cache[input] || ~input.indexOf('\'')/*中文没输完 233*/) {
if (this.cache[input] || ~input.indexOf("'") /*中文没输完 233*/) {
let options = this.cache[input] || [];
let combinedOptions = this.mergeOptions(options);
setOptions(combinedOptions);
return Promise.resolve({
options: combinedOptions,
options: combinedOptions
});
}
setLoading(true);
return isEffectiveApi(autoComplete, data) &&
return (
isEffectiveApi(autoComplete, data) &&
env
.fetcher(autoComplete, {
...data,
@ -126,26 +123,25 @@ export default class SelectControl extends React.Component<SelectProps, any> {
value: input
})
.then(ret => {
let options = ret.data && (ret.data as any).options || ret.data || [];
let options = (ret.data && (ret.data as any).options) || ret.data || [];
this.cache[input] = options;
let combinedOptions = this.mergeOptions(options);
setOptions(combinedOptions);
return Promise.resolve({
options: combinedOptions,
options: combinedOptions
});
})
.finally(() => setLoading(false));
.finally(() => setLoading(false))
);
}
mergeOptions(options: Array<object>) {
const {
selectedOptions,
} = this.props;
const {selectedOptions} = this.props;
let combinedOptions = options.concat();
if (Array.isArray(selectedOptions) && selectedOptions.length) {
selectedOptions.forEach((option) => {
selectedOptions.forEach(option => {
if (!find(combinedOptions, (item: Option) => item.value == option.value)) {
combinedOptions.push(option);
}
@ -155,10 +151,7 @@ export default class SelectControl extends React.Component<SelectProps, any> {
}
handleNewOptionClick(option: any) {
const {
setOptions,
options
} = this.props;
const {setOptions, options} = this.props;
let mergedOptions: Array<any> = options.concat();
mergedOptions.push({
@ -216,16 +209,15 @@ export default class SelectControl extends React.Component<SelectProps, any> {
}
@OptionsControl({
type: 'select',
type: 'select'
})
export class SelectControlRenderer extends SelectControl { };
export class SelectControlRenderer extends SelectControl {}
@OptionsControl({
type: 'multi-select',
type: 'multi-select'
})
export class MultiSelectControlRenderer extends SelectControl {
static defaultProps = {
multiple: true
};
};
}

View File

@ -1,13 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Renderer,
RendererProps
} from '../../factory';
import {Renderer, RendererProps} from '../../factory';
import BasicService from '../Service';
import { Schema } from '../../types';
import Scoped, { ScopedContext, IScopedContext } from '../../Scoped';
import { observer } from 'mobx-react';
import {Schema} from '../../types';
import Scoped, {ScopedContext, IScopedContext} from '../../Scoped';
import {observer} from 'mobx-react';
import {ServiceStore, IServiceStore} from '../../store/service';
@Renderer({
@ -15,7 +12,7 @@ import {ServiceStore, IServiceStore} from '../../store/service';
weight: -100,
storeType: ServiceStore.name,
storeExtendsData: false,
name: "service-control"
name: 'service-control'
})
export class ServiceRenderer extends BasicService {
static contextType = ScopedContext;
@ -30,10 +27,10 @@ export class ServiceRenderer extends BasicService {
scoped.unRegisterComponent(this);
}
renderBody():JSX.Element {
renderBody(): JSX.Element {
const {
render,
store,
render,
store,
body: schema,
controls,
tabs,
@ -44,12 +41,18 @@ export class ServiceRenderer extends BasicService {
classnames: cx
} = this.props;
const finnalSchema = store.schema || schema || {
controls,
tabs,
feildSet
};
if (finnalSchema && !finnalSchema.type && (finnalSchema.controls || finnalSchema.tabs || finnalSchema.feildSet) && renderFormItems) {
const finnalSchema = store.schema ||
schema || {
controls,
tabs,
feildSet
};
if (
finnalSchema &&
!finnalSchema.type &&
(finnalSchema.controls || finnalSchema.tabs || finnalSchema.feildSet) &&
renderFormItems
) {
return (
<div className={cx(`Form--${formMode || 'normal'}`)}>
{renderFormItems(finnalSchema, 'controls', {

View File

@ -1,8 +1,5 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import {TableCell} from '../Table';
import PopOver from '../PopOver';
@ -15,7 +12,7 @@ export interface StaticProps extends FormControlProps {
placeholder?: string;
tpl?: string;
text?: string;
};
}
export default class StaticControl extends React.Component<StaticProps, any> {
static defaultProps = {
@ -29,33 +26,25 @@ export default class StaticControl extends React.Component<StaticProps, any> {
}
handleQuickChange(values: any, saveImmediately: boolean | any) {
const {
onBulkChange,
onAction,
data
} = this.props;
const {onBulkChange, onAction, data} = this.props;
onBulkChange(values, saveImmediately === true);
if (saveImmediately && saveImmediately.api) {
onAction(null, {
actionType: 'ajax',
api: saveImmediately.api
}, extendObject(data, values));
onAction(
null,
{
actionType: 'ajax',
api: saveImmediately.api
},
extendObject(data, values)
);
}
}
render() {
const {
className,
value,
label,
type,
render,
children,
...rest
} = this.props;
const {className, value, label, type, render, children, ...rest} = this.props;
const subType = /^static/.test(type) ? (type.substring(7) || 'tpl') : type;
const subType = /^static/.test(type) ? type.substring(7) || 'tpl' : type;
const field = {
label,
@ -66,17 +55,21 @@ export default class StaticControl extends React.Component<StaticProps, any> {
return (
<div className="form-control-static">
{render('field', {
...field,
type: 'static-field',
field
}, {
value,
className,
onQuickChange: this.handleQuickChange,
})}
{render(
'field',
{
...field,
type: 'static-field',
field
},
{
value,
className,
onQuickChange: this.handleQuickChange
}
)}
</div>
);;
);
}
}
@ -85,12 +78,12 @@ export default class StaticControl extends React.Component<StaticProps, any> {
if (/(^|\/)form(?:\/.+)?\/control\/static(\-[^\/]+)?$/.test(path)) {
return true;
} else if (
/(^|\/)form(?:\/.+)?\/control\/[^\/]+$/.test(path)
&& schema
&& schema.type
&& (schema.name || schema.label)
&& resolveRenderer
&& resolveRenderer(`${path}/static-field/${schema.type}`)
/(^|\/)form(?:\/.+)?\/control\/[^\/]+$/.test(path) &&
schema &&
schema.type &&
(schema.name || schema.label) &&
resolveRenderer &&
resolveRenderer(`${path}/static-field/${schema.type}`)
) {
// 不一定
return true;
@ -100,13 +93,12 @@ export default class StaticControl extends React.Component<StaticProps, any> {
weight: -90,
strictMode: false,
sizeMutable: false,
name: "static-control"
name: 'static-control'
})
export class StaticControlRenderer extends StaticControl {};
export class StaticControlRenderer extends StaticControl {}
@Renderer({
test: /(^|\/)static\-field$/,
test: /(^|\/)static\-field$/
})
@QuickEdit()
@PopOver()
@ -140,14 +132,16 @@ export class StaticFieldRenderer extends TableCell {
const schema = {
...field,
className: inputClassName,
type: field && field.type || 'plain',
type: (field && field.type) || 'plain'
};
let body = children ? children : render('field', schema, {
...rest,
value,
data
});
let body = children
? children
: render('field', schema, {
...rest,
value,
data
});
if (width) {
style = style || {};
@ -159,14 +153,9 @@ export class StaticFieldRenderer extends TableCell {
}
return (
<Component
style={style}
className={className}
tabIndex={tabIndex}
onKeyUp={onKeyUp}
>
<Component style={style} className={className} tabIndex={tabIndex} onKeyUp={onKeyUp}>
{body}
</Component>
)
);
}
};
}

View File

@ -1,12 +1,9 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import omit = require('lodash/omit');
import pick = require('lodash/pick');
import { createObject } from '../../utils/helper';
import {createObject} from '../../utils/helper';
export interface SubFormProps extends FormControlProps {
placeholder?: string;
@ -14,26 +11,25 @@ export interface SubFormProps extends FormControlProps {
minLength?: number;
maxLength?: number;
labelField?: string;
};
}
export interface SubFormState {
openedIndex: number;
optionIndex: number;
};
}
let dom:HTMLElement;
const stripTag = (value:string) => {
if (!value) {
return value;
}
dom = dom || document.createElement('div');
dom.innerHTML = value;
return dom.innerText;
let dom: HTMLElement;
const stripTag = (value: string) => {
if (!value) {
return value;
}
dom = dom || document.createElement('div');
dom.innerHTML = value;
return dom.innerText;
};
export default class SubFormControl extends React.PureComponent<SubFormProps, SubFormState> {
static defaultProps:Partial<SubFormProps> = {
static defaultProps: Partial<SubFormProps> = {
minLength: 0,
maxLength: 0,
multiple: false,
@ -44,11 +40,11 @@ export default class SubFormControl extends React.PureComponent<SubFormProps, Su
btnLabel: '设置'
};
state:SubFormState = {
state: SubFormState = {
openedIndex: -1,
optionIndex: -1
};
constructor(props:SubFormProps) {
constructor(props: SubFormProps) {
super(props);
this.addItem = this.addItem.bind(this);
@ -71,7 +67,7 @@ export default class SubFormControl extends React.PureComponent<SubFormProps, Su
this.props.onChange(value);
}
removeItem(key:number, e: React.UIEvent<any>) {
removeItem(key: number, e: React.UIEvent<any>) {
e.stopPropagation();
e.preventDefault();
@ -86,7 +82,7 @@ export default class SubFormControl extends React.PureComponent<SubFormProps, Su
this.props.onChange(value);
}
open(index:number = 0) {
open(index: number = 0) {
this.setState({
openedIndex: index
});
@ -98,12 +94,8 @@ export default class SubFormControl extends React.PureComponent<SubFormProps, Su
});
}
handleDialogConfirm(values:Array<object>) {
const {
multiple,
onChange,
value
} = this.props;
handleDialogConfirm(values: Array<object>) {
const {multiple, onChange, value} = this.props;
if (multiple) {
let newValue = Array.isArray(value) ? value.concat() : [];
@ -123,10 +115,18 @@ export default class SubFormControl extends React.PureComponent<SubFormProps, Su
}
buildDialogSchema() {
let {
form } = this.props;
let {form} = this.props;
const dialogProps = ['title', 'actions', 'name', 'size', 'closeOnEsc', 'showCloseButton', 'bodyClassName', 'type'];
const dialogProps = [
'title',
'actions',
'name',
'size',
'closeOnEsc',
'showCloseButton',
'bodyClassName',
'type'
];
return {
...pick(form, dialogProps),
@ -149,34 +149,41 @@ export default class SubFormControl extends React.PureComponent<SubFormProps, Su
btnLabel
} = this.props;
return [(
return [
<div className={`${ns}SubForm-values`} key="values">
{Array.isArray(value) ? value.map((value:any, key) => (
<div
className={cx(`${ns}SubForm-value`, {
'is-disabled': disabled
}, editButtonClassName)}
key={key}
>
<span
data-tooltip="删除"
data-position="bottom"
className={`${ns}Select-valueIcon`}
onClick={this.removeItem.bind(this, key)}
>×</span>
<span
onClick={this.open.bind(this, key)}
className={`${ns}SubForm-valueLabel`}
data-tooltip="编辑详情"
data-position="bottom"
>
{value && labelField && value[labelField] && stripTag(value[labelField])|| btnLabel}
</span>
</div>
)) : null}
</div>
), (
{Array.isArray(value)
? value.map((value: any, key) => (
<div
className={cx(
`${ns}SubForm-value`,
{
'is-disabled': disabled
},
editButtonClassName
)}
key={key}
>
<span
data-tooltip="删除"
data-position="bottom"
className={`${ns}Select-valueIcon`}
onClick={this.removeItem.bind(this, key)}
>
×
</span>
<span
onClick={this.open.bind(this, key)}
className={`${ns}SubForm-valueLabel`}
data-tooltip="编辑详情"
data-position="bottom"
>
{(value && labelField && value[labelField] && stripTag(value[labelField])) ||
btnLabel}
</span>
</div>
))
: null}
</div>,
<button
key="add"
type="button"
@ -188,31 +195,28 @@ export default class SubFormControl extends React.PureComponent<SubFormProps, Su
<i className="fa fa-plus m-r-xs" />
<span></span>
</button>
)];
];
}
renderSingle() {
const {
classPrefix: ns,
btnClassName,
disabled,
value,
labelField,
btnLabel
} = this.props;
const {classPrefix: ns, btnClassName, disabled, value, labelField, btnLabel} = this.props;
return (
<div className={`${ns}SubForm-values`} key="values">
<div
className={cx(`${ns}SubForm-value`, {
'is-disabled': disabled
}, btnClassName)}
<div
className={cx(
`${ns}SubForm-value`,
{
'is-disabled': disabled
},
btnClassName
)}
onClick={this.open.bind(this, 0)}
data-tooltip="编辑详情"
data-position="bottom"
>
<span className={`${ns}SubForm-valueLabel`}>
{value && labelField && value[labelField] && stripTag(value[labelField])|| btnLabel}
{(value && labelField && value[labelField] && stripTag(value[labelField])) || btnLabel}
</span>
</div>
</div>
@ -220,33 +224,29 @@ export default class SubFormControl extends React.PureComponent<SubFormProps, Su
}
render() {
const {
multiple,
classPrefix: ns,
className,
render,
value,
data
} = this.props;
const {multiple, classPrefix: ns, className, render, value, data} = this.props;
const openedIndex = this.state.openedIndex;
return (
<div className={cx(`${ns}SubFormControl`, className)}>
{multiple ? this.renderMultipe() : this.renderSingle()}
{openedIndex !== -1 ? render(`dalog/${openedIndex}`, this.buildDialogSchema(), {
onClose: this.close,
onConfirm: this.handleDialogConfirm,
data: createObject(data, (multiple ? Array.isArray(value) && value[openedIndex] : value) || {})
}) : null}
{openedIndex !== -1
? render(`dalog/${openedIndex}`, this.buildDialogSchema(), {
onClose: this.close,
onConfirm: this.handleDialogConfirm,
data: createObject(
data,
(multiple ? Array.isArray(value) && value[openedIndex] : value) || {}
)
})
: null}
</div>
);
}
}
@FormItem({
type: 'form',
sizeMutable: false
})
export class SubFormControlRenderer extends SubFormControl {};
export class SubFormControlRenderer extends SubFormControl {}

View File

@ -1,8 +1,5 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import Switch from '../../components/Switch';
@ -10,14 +7,14 @@ export interface SwitchProps extends FormControlProps {
option?: string;
trueValue?: any;
falseValue?: any;
};
}
export default class SwitchControl extends React.Component<SwitchProps, any> {
static defaultProps = {
trueValue: true,
falseValue: false,
optionAtLeft: false
}
};
render() {
const {
className,
@ -29,12 +26,12 @@ export default class SwitchControl extends React.Component<SwitchProps, any> {
option,
onChange,
disabled,
optionAtLeft,
optionAtLeft
} = this.props;
return (
<div className={cx(`SwitchControl`, className)}>
{optionAtLeft ? (<span className={cx("Switch-option")}>{option}</span>) : null}
{optionAtLeft ? <span className={cx('Switch-option')}>{option}</span> : null}
<Switch
classPrefix={ns}
@ -45,16 +42,14 @@ export default class SwitchControl extends React.Component<SwitchProps, any> {
onChange={onChange}
/>
{optionAtLeft ? null : (<span className={cx("Switch-option")}>{option}</span>)}
{optionAtLeft ? null : <span className={cx('Switch-option')}>{option}</span>}
</div>
);
}
}
@FormItem({
type: 'switch',
sizeMutable: false
})
export class SwitchControlRenderer extends SwitchControl {};
export class SwitchControlRenderer extends SwitchControl {}

View File

@ -1,16 +1,13 @@
import React from 'react';
import {
FormItem,
FormControlProps
} from './Item';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import Button from '../../components/Button';
import {createObject, isObjectShallowModified} from '../../utils/helper';
import { RendererData, Action, Api, Payload } from '../../types';
import { isEffectiveApi } from '../../utils/api';
import { filter } from '../../utils/tpl';
import {RendererData, Action, Api, Payload} from '../../types';
import {isEffectiveApi} from '../../utils/api';
import {filter} from '../../utils/tpl';
import omit = require('lodash/omit');
import { dataMapping } from '../../utils/tpl-builtin';
import {dataMapping} from '../../utils/tpl-builtin';
import findIndex = require('lodash/findIndex');
export interface TableProps extends FormControlProps {
@ -36,14 +33,14 @@ export interface TableProps extends FormControlProps {
scaffold?: any;
deleteConfirmText?: string;
valueField?: string;
};
}
export interface TableState {
columns: Array<any>;
editIndex: number;
editting?: any;
isCreateMode?: boolean;
};
}
export default class FormTable extends React.Component<TableProps, TableState> {
static defaultProps = {
@ -54,28 +51,28 @@ export default class FormTable extends React.Component<TableProps, TableState> {
deleteBtnIcon: 'fa fa-minus',
confirmBtnIcon: 'fa fa-check',
cancelBtnIcon: 'fa fa-times',
valueField: ""
valueField: ''
};
static propsList: Array<string> = [
"onChange",
"name",
"columns",
"label",
"scaffold",
"showAddBtn",
"addable",
"removable",
"editable",
"addApi",
"updateApi",
"deleteApi"
'onChange',
'name',
'columns',
'label',
'scaffold',
'showAddBtn',
'addable',
'removable',
'editable',
'addApi',
'updateApi',
'deleteApi'
];
entries:Map<any, number>;
entries: Map<any, number>;
entityId: number = 1;
subForms:any = {};
constructor(props:TableProps) {
subForms: any = {};
constructor(props: TableProps) {
super(props);
this.state = {
@ -97,58 +94,45 @@ export default class FormTable extends React.Component<TableProps, TableState> {
this.entries.clear();
}
subFormRef(form:any, x:number, y:number) {
subFormRef(form: any, x: number, y: number) {
this.subForms[`${x}-${y}`] = form;
}
validate():any {
const {
value,
minLength,
maxLength
} = this.props;
validate(): any {
const {value, minLength, maxLength} = this.props;
if (minLength && (!Array.isArray(value) || value.length < minLength)) {
return `组合表单成员数量不够,低于最小的设定${minLength}个,请添加更多的成员。`;
} else if (maxLength && Array.isArray(value) && value.length > maxLength) {
return `组合表单成员数量超出,超出最大的设定${maxLength}个,请删除多余的成员。`;
} else {
const subForms:Array<any> = [];
const subForms: Array<any> = [];
Object.keys(this.subForms).forEach(key => this.subForms[key] && subForms.push(this.subForms[key]));
if (subForms.length) {
return Promise
.all(subForms.map(item => item.validate()))
.then((values) => {
if (~values.indexOf(false)) {
return '内部表单验证失败';
}
return;
})
return Promise.all(subForms.map(item => item.validate())).then(values => {
if (~values.indexOf(false)) {
return '内部表单验证失败';
}
return;
});
}
}
}
doAction(action:Action, ctx:RendererData, ...rest:Array<any>) {
const {
onAction,
value,
valueField,
env,
onChange,
editable,
} = this.props;
doAction(action: Action, ctx: RendererData, ...rest: Array<any>) {
const {onAction, value, valueField, env, onChange, editable} = this.props;
if (action.actionType === "add") {
if (action.actionType === 'add') {
const rows = Array.isArray(value) ? value.concat() : [];
if (action.payload) {
let toAdd = dataMapping(action.payload, ctx);
toAdd = Array.isArray(toAdd) ? toAdd : [toAdd];
toAdd.forEach((toAdd:any) => {
const idx = findIndex(rows, (item) => item[valueField as string] == toAdd[valueField as string]);
toAdd.forEach((toAdd: any) => {
const idx = findIndex(rows, item => item[valueField as string] == toAdd[valueField as string]);
if (~idx) {
rows.splice(idx, 1);
}
@ -156,7 +140,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
});
onChange(rows);
if (editable) {
this.startEdit(rows.length - 1, rows[rows.length - 1], true);
}
@ -166,7 +150,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
} else {
return this.addItem(rows.length - 1);
}
} else if (action.actionType === "remove" || action.actionType === "delete") {
} else if (action.actionType === 'remove' || action.actionType === 'delete') {
if (!valueField) {
return env.alert('请配置 valueField');
} else if (!action.payload) {
@ -174,11 +158,11 @@ export default class FormTable extends React.Component<TableProps, TableState> {
}
const rows = Array.isArray(value) ? value.concat() : [];
let toRemove:any = dataMapping(action.payload, ctx);
let toRemove: any = dataMapping(action.payload, ctx);
toRemove = Array.isArray(toRemove) ? toRemove : [toRemove];
toRemove.forEach((toRemove:any) => {
const idx = findIndex(rows, (item) => item[valueField as string] == toRemove[valueField as string]);
toRemove.forEach((toRemove: any) => {
const idx = findIndex(rows, item => item[valueField as string] == toRemove[valueField as string]);
if (~idx) {
rows.splice(idx, 1);
}
@ -192,11 +176,8 @@ export default class FormTable extends React.Component<TableProps, TableState> {
return onAction && onAction(action, ctx, ...rest);
}
addItem(index:number, payload:any = this.props.scaffold) {
const {
value,
onChange,
} = this.props;
addItem(index: number, payload: any = this.props.scaffold) {
const {value, onChange} = this.props;
let newValue = Array.isArray(value) ? value.concat() : [];
newValue.splice(index + 1, 0, {
...payload
@ -206,27 +187,19 @@ export default class FormTable extends React.Component<TableProps, TableState> {
this.startEdit(index, newValue[index], true);
}
startEdit(index:number, editting?:any, isCreate: boolean = false) {
startEdit(index: number, editting?: any, isCreate: boolean = false) {
const value = this.props.value;
const scaffold = this.props.scaffold;
this.setState({
editIndex: index,
editting: editting || value && value[index] || scaffold || {},
editting: editting || (value && value[index]) || scaffold || {},
isCreateMode: isCreate,
columns: this.state.isCreateMode === isCreate ? this.state.columns : this.buildColumns(this.props, isCreate)
})
});
}
async confirmEdit() {
const {
value,
onChange,
scaffold,
addApi,
updateApi,
data,
env
} = this.props;
const {value, onChange, scaffold, addApi, updateApi, data, env} = this.props;
let newValue = Array.isArray(value) ? value.concat() : [];
let item = {
...this.state.editting
@ -234,8 +207,8 @@ export default class FormTable extends React.Component<TableProps, TableState> {
const origin = newValue[this.state.editIndex];
const isNew = !isObjectShallowModified(scaffold, origin, false);
let remote:Payload | null = null;
let remote: Payload | null = null;
if (isNew && isEffectiveApi(addApi, createObject(data, item))) {
remote = await env.fetcher(addApi, createObject(data, item));
} else if (isEffectiveApi(updateApi, createObject(data, item))) {
@ -249,7 +222,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
item = {
...item,
...remote.data
}
};
}
newValue.splice(this.state.editIndex, 1, item);
@ -262,10 +235,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
}
cancelEdit() {
const {
value,
onChange
} = this.props;
const {value, onChange} = this.props;
if (this.state.isCreateMode) {
let newValue = Array.isArray(value) ? value.concat() : [];
@ -278,15 +248,8 @@ export default class FormTable extends React.Component<TableProps, TableState> {
});
}
async removeItem(index:number) {
const {
value,
onChange,
deleteApi,
deleteConfirmText,
env,
data
} = this.props;
async removeItem(index: number) {
const {value, onChange, deleteApi, deleteConfirmText, env, data} = this.props;
let newValue = Array.isArray(value) ? value.concat() : [];
const item = newValue[index];
@ -296,12 +259,12 @@ export default class FormTable extends React.Component<TableProps, TableState> {
const ctx = createObject(data, item);
if (isEffectiveApi(deleteApi, ctx)) {
const confirmed = await env.confirm(deleteConfirmText ? filter(deleteConfirmText, ctx): '确认要删除?')
if (!confirmed) { // 如果不确认,则跳过!
const confirmed = await env.confirm(deleteConfirmText ? filter(deleteConfirmText, ctx) : '确认要删除?');
if (!confirmed) {
// 如果不确认,则跳过!
return;
}
const result = await env.fetcher(deleteApi, ctx);
if (!result.ok) {
@ -315,7 +278,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
onChange(newValue);
}
buildItemProps(item:any, index:number) {
buildItemProps(item: any, index: number) {
if (!this.props.editable) {
return null;
}
@ -325,151 +288,125 @@ export default class FormTable extends React.Component<TableProps, TableState> {
};
}
buildColumns(props:TableProps, isCreateMode = false):Array<any> {
buildColumns(props: TableProps, isCreateMode = false): Array<any> {
const env = this.props.env;
let columns:Array<any> = Array.isArray(props.columns) ? props.columns.concat() : [];
let columns: Array<any> = Array.isArray(props.columns) ? props.columns.concat() : [];
const ns = this.props.classPrefix;
let btns = [];
if (props.addable && props.showAddBtn !== false) {
btns.push({
children: ({
key,
rowIndex
}: {
key: any,
rowIndex: number
}) => ~this.state.editIndex ? null : (
<Button
classPrefix={ns}
size="sm"
key={key}
level="link"
tooltip="新增一行"
tooltipContainer={env && env.getModalContainer ? env.getModalContainer() : undefined}
onClick={this.addItem.bind(this, rowIndex, undefined)}
>
{props.addBtnLabel ? (<span>{props.addBtnLabel}</span>) : null}
{props.addBtnIcon ? (<i className={props.addBtnIcon} />) : null}
</Button>
)
})
children: ({key, rowIndex}: {key: any; rowIndex: number}) =>
~this.state.editIndex ? null : (
<Button
classPrefix={ns}
size="sm"
key={key}
level="link"
tooltip="新增一行"
tooltipContainer={env && env.getModalContainer ? env.getModalContainer() : undefined}
onClick={this.addItem.bind(this, rowIndex, undefined)}
>
{props.addBtnLabel ? <span>{props.addBtnLabel}</span> : null}
{props.addBtnIcon ? <i className={props.addBtnIcon} /> : null}
</Button>
)
});
}
if (props.editable) {
columns = columns.map(column => {
const quickEdit = !isCreateMode && column.hasOwnProperty('quickEditOnUpdate')
? column.quickEditOnUpdate
: column.quickEdit;
const quickEdit =
!isCreateMode && column.hasOwnProperty('quickEditOnUpdate')
? column.quickEditOnUpdate
: column.quickEdit;
return quickEdit === false ? omit(column, ["quickEdit"]) : {
...column,
quickEdit: {
type: 'text',
...quickEdit,
saveImmediately: true,
mode: 'inline'
}
};
return quickEdit === false
? omit(column, ['quickEdit'])
: {
...column,
quickEdit: {
type: 'text',
...quickEdit,
saveImmediately: true,
mode: 'inline'
}
};
});
btns.push({
children: ({
key,
rowIndex,
data
}: {
key: any;
rowIndex: number;
data: any;
}) => ~this.state.editIndex || data && data.__isPlaceholder ? null : (
<Button
classPrefix={ns}
size="sm"
key={key}
level="link"
tooltip="编辑当前行"
tooltipContainer={env && env.getModalContainer ? env.getModalContainer() : undefined}
onClick={() => this.startEdit(rowIndex)}
>
{props.updateBtnLabel ? (<span>{props.updateBtnLabel}</span>) : null}
{props.updateBtnIcon ? (<i className={props.updateBtnIcon} />) : null}
</Button>
)
})
children: ({key, rowIndex, data}: {key: any; rowIndex: number; data: any}) =>
~this.state.editIndex || (data && data.__isPlaceholder) ? null : (
<Button
classPrefix={ns}
size="sm"
key={key}
level="link"
tooltip="编辑当前行"
tooltipContainer={env && env.getModalContainer ? env.getModalContainer() : undefined}
onClick={() => this.startEdit(rowIndex)}
>
{props.updateBtnLabel ? <span>{props.updateBtnLabel}</span> : null}
{props.updateBtnIcon ? <i className={props.updateBtnIcon} /> : null}
</Button>
)
});
btns.push({
children: ({
key,
rowIndex
}: {
key: any;
rowIndex: number;
}) => this.state.editIndex === rowIndex ? (
<Button
classPrefix={ns}
size="sm"
key={key}
level="link"
tooltip="保存"
tooltipContainer={env && env.getModalContainer ? env.getModalContainer() : undefined}
onClick={this.confirmEdit}
>
{props.confirmBtnLabel ? (<span>{props.confirmBtnLabel}</span>) : null}
{props.confirmBtnIcon ? (<i className={props.confirmBtnIcon} />) : null}
</Button>
) : null
})
children: ({key, rowIndex}: {key: any; rowIndex: number}) =>
this.state.editIndex === rowIndex ? (
<Button
classPrefix={ns}
size="sm"
key={key}
level="link"
tooltip="保存"
tooltipContainer={env && env.getModalContainer ? env.getModalContainer() : undefined}
onClick={this.confirmEdit}
>
{props.confirmBtnLabel ? <span>{props.confirmBtnLabel}</span> : null}
{props.confirmBtnIcon ? <i className={props.confirmBtnIcon} /> : null}
</Button>
) : null
});
btns.push({
children: ({
key,
rowIndex
}: {
key: any,
rowIndex: number
}) => this.state.editIndex === rowIndex ? (
<Button
classPrefix={ns}
size="sm"
key={key}
level="link"
tooltip="取消"
tooltipContainer={env && env.getModalContainer ? env.getModalContainer() : undefined}
onClick={this.cancelEdit}
>
{props.cancelBtnLabel ? (<span>{props.cancelBtnLabel}</span>) : null}
{props.cancelBtnIcon ? (<i className={props.cancelBtnIcon} />) : null}
</Button>
) : null
})
children: ({key, rowIndex}: {key: any; rowIndex: number}) =>
this.state.editIndex === rowIndex ? (
<Button
classPrefix={ns}
size="sm"
key={key}
level="link"
tooltip="取消"
tooltipContainer={env && env.getModalContainer ? env.getModalContainer() : undefined}
onClick={this.cancelEdit}
>
{props.cancelBtnLabel ? <span>{props.cancelBtnLabel}</span> : null}
{props.cancelBtnIcon ? <i className={props.cancelBtnIcon} /> : null}
</Button>
) : null
});
}
if (props.removable) {
btns.push({
children: ({
key,
rowIndex,
data
}: {
key: any,
rowIndex: number,
data: any
}) => ~this.state.editIndex || data && data.__isPlaceholder ? null : (
<Button
classPrefix={ns}
size="sm"
key={key}
level="link"
tooltip="删除当前行"
tooltipContainer={env && env.getModalContainer ? env.getModalContainer() : undefined}
onClick={this.removeItem.bind(this, rowIndex)}
>
{props.deleteBtnLabel ? (<span>{props.deleteBtnLabel}</span>) : null}
{props.deleteBtnIcon ? (<i className={props.deleteBtnIcon} />) : null}
</Button>
)
})
children: ({key, rowIndex, data}: {key: any; rowIndex: number; data: any}) =>
~this.state.editIndex || (data && data.__isPlaceholder) ? null : (
<Button
classPrefix={ns}
size="sm"
key={key}
level="link"
tooltip="删除当前行"
tooltipContainer={env && env.getModalContainer ? env.getModalContainer() : undefined}
onClick={this.removeItem.bind(this, rowIndex)}
>
{props.deleteBtnLabel ? <span>{props.deleteBtnLabel}</span> : null}
{props.deleteBtnIcon ? <i className={props.deleteBtnIcon} /> : null}
</Button>
)
});
}
if (btns.length) {
@ -478,17 +415,14 @@ export default class FormTable extends React.Component<TableProps, TableState> {
buttons: btns,
width: 100,
label: '操作'
})
});
}
return columns;
}
handleTableSave(rows:Array<object> | object, diff:Array<object> | object, rowIndexes: Array<number> | number) {
const {
onChange,
value
} = this.props;
handleTableSave(rows: Array<object> | object, diff: Array<object> | object, rowIndexes: Array<number> | number) {
const {onChange, value} = this.props;
const newValue = Array.isArray(value) ? value.concat() : [];
@ -524,23 +458,21 @@ export default class FormTable extends React.Component<TableProps, TableState> {
onChange(newValue);
}
handleSaveTableOrder(moved:Array<object>, rows:Array<object>) {
const {
onChange
} = this.props;
handleSaveTableOrder(moved: Array<object>, rows: Array<object>) {
const {onChange} = this.props;
onChange(rows.map((item:object) => ({...item})));
onChange(rows.map((item: object) => ({...item})));
}
removeEntry(entry:any) {
removeEntry(entry: any) {
if (this.entries.has(entry)) {
this.entries.delete(entry);
}
}
getEntryId(entry:any) {
getEntryId(entry: any) {
if (entry === this.state.editting) {
return "editing";
return 'editing';
} else if (!this.entries.has(entry)) {
this.entries.set(entry, this.entityId++);
}
@ -549,43 +481,44 @@ export default class FormTable extends React.Component<TableProps, TableState> {
}
render() {
const {
className,
value,
showAddBtn,
disabled,
render,
placeholder,
draggable,
addable
} = this.props;
const {className, value, showAddBtn, disabled, render, placeholder, draggable, addable} = this.props;
return (
return (
<div className={cx('form-control-table', className)}>
{render('body', {
type: 'table',
placeholder,
disabled,
columns: this.state.columns,
affixHeader: false
}, {
value: undefined,
draggable: draggable && !~this.state.editIndex,
items: (Array.isArray(value) && value.length ? value : addable && showAddBtn !== false ? [{__isPlaceholder: true}] : []).map((value:any, index:number) =>
index === this.state.editIndex ? this.state.editting : value),
getEntryId: this.getEntryId,
onSave: this.handleTableSave,
onSaveOrder: this.handleSaveTableOrder,
buildItemProps: this.buildItemProps,
quickEditFormRef: this.subFormRef
})}
{render(
'body',
{
type: 'table',
placeholder,
disabled,
columns: this.state.columns,
affixHeader: false
},
{
value: undefined,
draggable: draggable && !~this.state.editIndex,
items: (Array.isArray(value) && value.length
? value
: addable && showAddBtn !== false
? [{__isPlaceholder: true}]
: []
).map((value: any, index: number) =>
index === this.state.editIndex ? this.state.editting : value
),
getEntryId: this.getEntryId,
onSave: this.handleTableSave,
onSaveOrder: this.handleSaveTableOrder,
buildItemProps: this.buildItemProps,
quickEditFormRef: this.subFormRef
}
)}
</div>
);;
);
}
}
@FormItem({
test: /(^|\/)form(?:\/.+)?\/control\/table$/,
name: "table-control"
name: 'table-control'
})
export class TableControlRenderer extends FormTable {};
export class TableControlRenderer extends FormTable {}

View File

@ -1,13 +1,9 @@
import React from 'react';
import {
Renderer,
RendererProps
} from '../../factory';
import { Schema } from '../../types';
import {Renderer, RendererProps} from '../../factory';
import {Schema} from '../../types';
import Tabs from '../Tabs';
export interface TabsProps extends RendererProps {
};
export interface TabsProps extends RendererProps {}
@Renderer({
test: /(^|\/)form(?:.+)?\/control\/tabs$/i,
@ -19,21 +15,13 @@ export class TabsRenderer extends React.Component<TabsProps, any> {
mountOnEnter: false // form 中的不按需渲染
};
constructor(props:TabsProps) {
constructor(props: TabsProps) {
super(props);
this.renderTab = this.renderTab.bind(this);
this.renderTab = this.renderTab.bind(this);
}
renderTab(tab:any, {key}:any) {
const {
renderFormItems,
formMode,
formHorizontal,
$path,
render,
classnames: cx
} = this.props;
renderTab(tab: any, {key}: any) {
const {renderFormItems, formMode, formHorizontal, $path, render, classnames: cx} = this.props;
if (renderFormItems && !tab.type && (tab.controls || tab.fieldSet || tab.tabs)) {
return (
@ -46,22 +34,12 @@ export class TabsRenderer extends React.Component<TabsProps, any> {
);
}
return render(`tab/${key}`, tab.body || tab.tab || tab);
}
render() {
const {
children,
type,
...rest
} = this.props;
const {children, type, ...rest} = this.props;
return (
<Tabs
{...rest}
tabRender={this.renderTab}
/>
);
return <Tabs {...rest} tabRender={this.renderTab} />;
}
}

Some files were not shown because too many files have changed in this diff Show More