mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-30 11:07:52 +08:00
prettier files
This commit is contained in:
parent
af7fe6a09b
commit
0b5e4cb2ea
@ -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;
|
||||
|
115
src/compat.ts
115
src/compat.ts
@ -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;
|
||||
});
|
||||
});
|
||||
|
@ -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}>
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -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`
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -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 (
|
||||
|
@ -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);
|
||||
|
@ -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'
|
||||
})
|
||||
);
|
||||
|
@ -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);
|
||||
|
@ -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'
|
||||
})
|
||||
);
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
>
|
||||
|
@ -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
|
||||
)}
|
||||
|
@ -20,7 +20,7 @@ export interface HtmlProps {
|
||||
|
||||
export class Html extends React.Component<HtmlProps> {
|
||||
static defaultProps = {
|
||||
inline: true,
|
||||
inline: true
|
||||
};
|
||||
|
||||
dom: any;
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
)}
|
||||
|
@ -19,7 +19,7 @@ Position.prototype.updatePosition = function(target: any) {
|
||||
positionLeft: 0,
|
||||
positionTop: 0,
|
||||
arrowOffsetLeft: null,
|
||||
arrowOffsetTop: null,
|
||||
arrowOffsetTop: null
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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'
|
||||
})
|
||||
);
|
||||
|
@ -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'
|
||||
})
|
||||
);
|
||||
|
@ -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 (
|
||||
|
@ -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'
|
||||
};
|
||||
|
@ -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'
|
||||
})
|
||||
);
|
||||
|
@ -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
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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')
|
||||
};
|
||||
|
@ -23,7 +23,7 @@ interface TooltipProps extends React.HTMLProps<HTMLDivElement> {
|
||||
|
||||
export class Tooltip extends React.Component<TooltipProps> {
|
||||
static defaultProps = {
|
||||
className: '',
|
||||
className: ''
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@ -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'
|
||||
})
|
||||
);
|
||||
|
@ -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}>
|
||||
|
@ -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
|
||||
}
|
||||
};
|
||||
|
@ -75,5 +75,5 @@ export {
|
||||
toast,
|
||||
Tooltip,
|
||||
TooltipWrapper,
|
||||
Tree,
|
||||
Tree
|
||||
};
|
||||
|
588
src/factory.tsx
588
src/factory.tsx
@ -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);
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
@ -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() {
|
||||
|
@ -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 {}
|
||||
|
@ -6,6 +6,6 @@ export default ButtonGroup;
|
||||
|
||||
@Renderer({
|
||||
test: /(^|\/)(?:button|action)\-group$/,
|
||||
name: 'button-group',
|
||||
name: 'button-group'
|
||||
})
|
||||
export class ButtonGroupRenderer extends ButtonGroup {}
|
||||
|
@ -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 {}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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 {}
|
||||
|
@ -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;
|
||||
|
@ -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 {}
|
||||
|
@ -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<{}> {}
|
||||
|
@ -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'
|
||||
};
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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 {}
|
||||
|
@ -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)) {
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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>;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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
@ -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
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -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'
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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>×</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>×</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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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;
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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'
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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', {
|
||||
|
@ -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>
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user