/** * Created by buhi on 2017/7/26. */ import * as React from "react" import { FormFieldSchema, FieldListens} from "./form"; import { change, Field, formValueSelector, getFormValues } from "redux-form" import {renderFields} from "./render-fields"; import {connect} from "react-redux"; import {createSelector} from "reselect"; export type WidgetProps = { fieldSchema?:FormFieldSchema, renderField?:typeof StatelessField keyPath:string meta?:{ active:boolean asyncValidating:boolean autofilled:boolean dirty:boolean dispatch:(a:any)=>void error:any form:string invalid:boolean pristine:boolean submitFailed:boolean submitting:boolean touched:boolean valid:boolean visited:boolean warning:any }, input?:{ name:string onBlur:(...args:any[])=>void onChange:(...args:any[])=>void onDragStart:(...args:any[])=>void onDrop:(...args:any[])=>void onFocus:(...args:any[])=>void value:any } hide?:boolean [rest:string]:any onSchemaChange:(changes:Partial[]|Promise[]>)=>void } type Widget = React.StatelessComponent | React.ComponentClass export function addType(name:string):(widget:Widget)=>void export function addType(name:string,widget:Widget):void export function addType(name:string,widget?:Widget) { function addWidgetTypeToRegistration(widget:Widget):any { customTypes.set(name, (props:WidgetProps) =>
); return widget; } return widget?addWidgetTypeToRegistration(widget):addWidgetTypeToRegistration; } export function addTypeWithWrapper(name:string,widget:Widget){ customTypes.set(name,widget); } let customTypes = new Map(); export function clearTypes(){ customTypes.clear() } export function getType(name:string):Widget{ return customTypes.get(name) } export function preRenderField(field:FormFieldSchema, form:string, keyPath:string):React.ReactNode{ //keyPath is the old keyPath const key = field.key||field.label if(!key) console.warn("必须为此schema设置一个key或者label, 以作为React的key:",field) if(field.listens && ( typeof field.listens === 'function' || Object.keys(field.listens).length)) return ; else return ; } export function getComponentProps(field:FormFieldSchema){ const { hide, type, key, label, options, fullWidth, component, normalize, props, warn, withRef, style, children, onChange, listens, onFileChange, validate, format, parse, multiple, value, ...rest } = field; return rest } export class StatelessField extends React.PureComponent<{field:FormFieldSchema, form:string, keyPath:string}>{ render() { const {field, form, keyPath} = this.props; let { hide, type, key, label, options, fullWidth, style, children, ...rest } = field; if (field.hide) return null; let typeName = field.type; if (typeof field.type !== 'string') typeName = ""; const CustomWidget = customTypes.get(type); const className = "field " + typeName + (fullWidth?" full-width":"") + (rest.required?" required":"") + (rest.disabled?" disabled":"") if (CustomWidget) { return
} else if (typeof type === 'function') return
; switch (type) { //这里不可能存在getChildren还没有被执行的情况 case "virtual-group": return renderFields(form, children, keyPath, true) case "group": return
{label} { renderFields(form, children, keyPath) }
; default: return
不支持的字段类型:{JSON.stringify(field)}
} } } interface FieldNodeProps{ form:string, fieldSchema:FormFieldSchema, keyPath:string, listeners?:FieldListens, values?:any[], dispatch?:Function } @((connect as any)( (_:any,p:WidgetProps)=>{ let listeners = p.fieldSchema.listens; const formSelector = formValueSelector(p.form); return createSelector( listeners.map(({to})=>{ if(to instanceof Function) to = to(p.keyPath.split(".").slice(0,-1).join(".")) if(to instanceof Array){ return createSelector( to.map(key=>{ return (s:any)=>formSelector(s,key) as any }) as any, (...values:any[])=>{ return values } ) }else return (s:any)=>formSelector(s,to as string) as any }) as any, (...values:any[])=>{ return { values, listeners }; } ) as any }, (dispatch:any)=>({dispatch}) ) as any) class StatefulField extends React.PureComponent{ static contextTypes={ store:require("prop-types").object }; state=this.props.fieldSchema; componentDidMount(){ this.reload(this.props,true) } unmounted = false; componentWillUnmount(){ this.unmounted = true; } reload(props:FieldNodeProps,isInitializing?:boolean){ const state = this.context.store.getState(); Promise.all(Object.keys(props.listeners).map((_,i)=>{ const formValue = getFormValues(props.form)(state); const res = props.listeners[i].then(props.values[i],formValue,props.dispatch); if(!(res instanceof Promise)) return Promise.resolve(res||{}); else return res; })).then(newSchemas=> { if(this.unmounted) return; let newSchema:FormFieldSchema&{value?:any} = newSchemas.reduce((old,newSchema)=>({...old,...newSchema||emptyObject}),props.fieldSchema); if(newSchema.hasOwnProperty("value") && (!isInitializing)){ newSchema = Object.assign({}, newSchema) props.dispatch(change(props.form,props.keyPath,newSchema.value)); delete newSchema['value'] } this.setState(newSchema); }) } componentDidUpdate(prevProps:FieldNodeProps){ if(prevProps.values === this.props.values && prevProps.form===this.props.form && prevProps.fieldSchema === this.props.fieldSchema) return; this.reload(this.props); } render(){ const {form,keyPath} = this.props; return } } const emptyObject = {}