feat: add vc-form demo

pull/22/head
tangjinzhou 2018-05-04 16:02:31 +08:00
parent 4f0c5b8ce1
commit 0658674d3f
13 changed files with 726 additions and 17 deletions

View File

@ -1,6 +1,8 @@
import Vue from 'vue'
import PropTypes from './vue-types'
import antRefDirective from './antRefDirective'
Vue.use(antRefDirective)
export default {
props: {

View File

@ -0,0 +1,12 @@
export default {
install: (Vue, options) => {
Vue.directive('ant-ref', {
bind: function (el, binding, vnode) {
binding.value(vnode)
},
unbind: function (el, binding, vnode) {
binding.value()
},
})
},
}

View File

@ -1,5 +1,9 @@
import Vue from 'vue'
import ConfirmDialog from './ConfirmDialog'
import antRefDirective from '../_util/antRefDirective'
Vue.use(antRefDirective)
export default function confirm (config) {
const div = document.createElement('div')
const el = document.createElement('div')

View File

@ -10,6 +10,8 @@ import Popup from './Popup'
import { getAlignFromPlacement, getPopupClassNameFromAlign, noop } from './utils'
import BaseMixin from '../_util/BaseMixin'
import { cloneElement } from '../_util/vnode'
import antRefDirective from '../_util/antRefDirective'
Vue.use(antRefDirective)
function returnEmptyString () {
return ''

View File

@ -0,0 +1,84 @@
/* eslint react/no-multi-comp:0, no-console:0 */
import BaseMixin from '../../_util/BaseMixin'
import createDOMForm from '../src/createDOMForm'
import { Modal } from 'antd'
import { regionStyle, errorStyle } from './styles'
const Form = {
mixins: [BaseMixin],
props: {
form: Object,
},
data () {
return {
visible: false,
}
},
methods: {
onSubmit (e) {
e.preventDefault()
this.form.validateFieldsAndScroll((error, values) => {
if (!error) {
console.log('ok', values)
} else {
console.log('error', error, values)
}
})
},
onCancel () {
this.setState({
visible: false,
})
},
open () {
this.setState({
visible: true,
})
},
},
render () {
const { getFieldProps, getFieldError } = this.form
return (<div style={{ margin: '20px' }}>
<h2>modal</h2>
<Modal
visible={this.visible}
bodyStyle={{
height: '200px',
overflow: 'auto',
}}
onCancel={this.onCancel}
title='modal'
>
<div ref='dialogContent'>
<form onSubmit={this.onSubmit}>
<input
{...getFieldProps('required', {
rules: [{
required: true,
message: '必填',
}],
})}
/>
<div style={errorStyle}>
{getFieldError('required') ? getFieldError('required').join(',')
: <b style={{ visibility: 'hidden' }}>1</b>}
</div>
<div style={{ marginTop: '300px' }}>
<button>submit</button>
</div>
</form>
</div>
</Modal>
<div style={ regionStyle }>
<button onClick={this.open}>open</button>
</div>
</div>)
},
}
export default createDOMForm()(Form)

View File

@ -0,0 +1,194 @@
/* eslint react/no-multi-comp:0, no-console:0 */
import createForm from '../src/createDOMForm'
const Form = {
props: {
form: Object,
},
methods: {
onSubmit (e) {
e.preventDefault()
console.log('Values of member[0].name.firstname and a[0][1].b.c[0]')
console.log(this.form.getFieldsValue(['member[0].name.firstname', 'a[0][1].b.c[0]']))
console.log('Values of all fields')
console.log(this.form.getFieldsValue())
this.form.validateFieldsAndScroll((error, values) => {
if (!error) {
console.log('ok', values)
} else {
console.log('error', error, values)
}
})
},
onChange (e) {
console.log(e.target.value)
},
setField () {
this.form.setFieldsValue({
member: [
{
name: {
firstname: 'm1 first',
lastname: 'm1 last',
},
},
{
name: {
firstname: 'm2 first',
lastname: 'm2 last',
},
},
],
a: [
[undefined, {
b: {
c: ['Value of a[0][1].b.c[0]'],
},
}],
],
w: {
x: {
y: {
z: ['Value of w.x.y.z[0]'],
},
},
},
})
},
resetFields () {
console.log('reset')
this.form.resetFields()
},
},
render () {
const { getFieldDecorator, getFieldError } = this.form
return (
<form onSubmit={this.onSubmit}>
<div>Member 0 firstname</div>
{getFieldDecorator('member[0].name.firstname', {
initialValue: '',
rules: [{
required: true,
message: 'What\'s the member_0 firstname?',
}],
})(
<input
onInput={this.onChange}
/>
)}
<div style={{ color: 'red' }}>
{(getFieldError('member[0].name.firstname') || []).join(', ')}
</div>
<div>Member 0 lastname</div>
{getFieldDecorator('member[0].name.lastname', {
initialValue: '',
rules: [{
required: true,
message: 'What\'s the member_0 lastname?',
}],
})(
<input
onInput={this.onChange}
/>
)}
<div style={{ color: 'red' }}>
{(getFieldError('member[0].name.firstname') || []).join(', ')}
</div>
<div>Member 1 firstname</div>
{getFieldDecorator('member[1].name.firstname', {
initialValue: '',
rules: [{
required: true,
message: 'What\'s the member_1 fistname?',
}],
})(
<input
onInput={this.onChange}
/>
)}
<div style={{ color: 'red' }}>
{(getFieldError('member[1].name.firstname') || []).join(', ')}
</div>
<div>Member 1 lastname</div>
{getFieldDecorator('member[1].name.lastname', {
initialValue: '',
rules: [{
required: true,
message: 'What\'s the member_1 lastname?',
}],
})(
<input
onInput={this.onChange}
/>
)}
<div style={{ color: 'red' }}>
{(getFieldError('member[1].name.firstname') || []).join(', ')}
</div>
<div>a[0][1].b.c[0]</div>
{getFieldDecorator('a[0][1].b.c[0]', {
initialValue: '',
rules: [{
required: true,
message: 'What\'s a[0][1].b.c[0]?',
}],
})(
<input
onInput={this.onChange}
/>
)}
<div style={{ color: 'red' }}>
{(getFieldError('a[0][1].b.c[0]') || []).join(', ')}
</div>
<div>w.x.y.z[0]</div>
{getFieldDecorator('w.x.y.z[0]', {
initialValue: '',
rules: [{
required: true,
message: 'What\'s w.x.y.z[0]?',
}],
})(
<input
onInput={this.onChange}
/>
)}
<div style={{ color: 'red' }}>
{(getFieldError('w.x.y.z[0]') || []).join(', ')}
</div>
<button onClick={this.setField}>Set field</button>
<button onClick={this.resetFields}>Reset fields</button>
<button>Submit</button>
</form>
)
},
}
const NewForm = createForm({
onFieldsChange (_, changedFields, allFields) {
console.log('onFieldsChange: ', changedFields, allFields)
},
onValuesChange (_, changedValues, allValues) {
console.log('onValuesChange: ', changedValues, allValues)
},
})(Form)
export default {
render () {
return (<div>
<h2>setFieldsValue</h2>
<NewForm />
</div>)
},
}

View File

@ -0,0 +1,151 @@
/* eslint react/no-multi-comp:0, no-console:0 */
import { createForm } from '../index'
import { regionStyle, errorStyle } from './styles'
const CustomInput = {
props: {
form: Object,
},
data () {
return {
data: [],
}
},
methods: {
checkUpper (rule, value = '', callback) {
if (value !== value.toUpperCase()) {
callback(new Error('need to be upper!'))
} else {
callback()
}
},
toUpper (v, prev) {
if (v === prev) {
return v
}
return v.toUpperCase()
},
},
render () {
const { getFieldProps, getFieldError } = this.form
const errors = getFieldError('upper')
return (<div style={ regionStyle }>
<div>upper normalize</div>
<div>
<input {...getFieldProps('upper', {
normalize: this.toUpper,
rules: [{
validator: this.checkUpper,
}],
})}
/>
</div>
<div style={errorStyle}>
{(errors) ? errors.join(',') : null}
</div>
</div>)
},
}
const MaxMin = {
props: {
form: Object,
},
methods: {
normalizeMin (value, prevValue, allValues) {
console.log('normalizeMin', allValues.min, allValues.max)
const previousAllValues = this.form.getFieldsValue()
if (allValues.max !== previousAllValues.max) {
// max changed
if (value === '' || Number(allValues.max) < Number(value)) {
return allValues.max
}
}
return value
},
normalizeMax (value, prevValue, allValues) {
console.log('normalizeMax', allValues.min, allValues.max)
const previousAllValues = this.form.getFieldsValue()
if (allValues.min !== previousAllValues.min) {
// min changed
if (value === '' || Number(allValues.min) > Number(value)) {
return allValues.min
}
}
return value
},
},
render () {
const { getFieldProps } = this.form
return (<div style={ regionStyle }>
<div>min: <select
{...getFieldProps('min', {
normalize: this.normalizeMin,
initialValue: '',
})}
>
<option value=''>empty</option>
<option value='1'>1</option>
<option value='2'>2</option>
<option value='3'>3</option>
<option value='4'>4</option>
<option value='5'>5</option>
</select>
</div>
<div>max: <select
{...getFieldProps('max', {
initialValue: '',
normalize: this.normalizeMax,
})}
>
<option value=''>empty</option>
<option value='1'>1</option>
<option value='2'>2</option>
<option value='3'>3</option>
<option value='4'>4</option>
<option value='5'>5</option>
</select>
</div>
</div>)
},
}
const Form = {
// props: {
// form: Object,
// },
methods: {
onSubmit (e) {
e.preventDefault()
this.form.validateFields((error, values) => {
if (!error) {
console.log('ok', values)
} else {
console.log('error', error, values)
}
})
},
},
render () {
const { form } = this
return (<div style={{ margin: '20px' }}>
<h2>normalize</h2>
<form onSubmit={this.onSubmit}>
<CustomInput form={ form }/>
<MaxMin form={ form }/>
<div style={ regionStyle }>
<button>submit</button>
</div>
</form>
</div>)
},
}
export default createForm()(Form)

View File

@ -0,0 +1,259 @@
/* eslint react/no-multi-comp:0, no-console:0 */
import createDOMForm from '../src/createDOMForm'
import { DatePicker, Select } from 'antd'
import { regionStyle, errorStyle } from './styles'
const { Option } = Select
const Email = {
props: {
form: Object,
},
render () {
const { getFieldProps, getFieldError, isFieldValidating } = this.form
const errors = getFieldError('email')
return (<div style={ regionStyle }>
<div>email sync validate</div>
<div>
<input {...getFieldProps('email', {
initialValue: '',
rules: [
{
type: 'email',
message: <b key='err'>错误的 email 格式</b>,
},
],
})}
/></div>
<div style={errorStyle}>
{errors}
</div>
<div style={errorStyle}>
{isFieldValidating('email') ? 'validating' : null}
</div>
</div>)
},
}
const User = {
props: {
form: Object,
},
methods: {
userExists (rule, value, callback) {
setTimeout(() => {
if (value === '1') {
callback([new Error('are you kidding?')])
} else if (value === 'yiminghe') {
callback([new Error('forbid yiminghe')])
} else {
callback()
}
}, 300)
},
},
render () {
const { getFieldProps, getFieldError, isFieldValidating } = this.form
const errors = getFieldError('user')
return (<div style={ regionStyle }>
<div><span style={{ color: 'red' }}>*</span> user async validate</div>
<div>
<input {...getFieldProps('user', {
initialValue: '',
validateFirst: true,
rules: [
{
required: true,
},
{
validator: this.userExists,
},
],
})}
/></div>
<div style={errorStyle}>
{(errors) ? errors.join(',') : null}
</div>
<div style={errorStyle}>
{isFieldValidating('user') ? 'validating' : null}
</div>
</div>)
},
}
const CustomInput = {
props: {
form: Object,
},
render () {
const { getFieldProps, getFieldError, isFieldValidating } = this.form
const errors = getFieldError('select')
return (<div style={ regionStyle }>
<div><span style={{ color: 'red' }}>*</span> custom select sync validate</div>
<div><Select
placeholder='please select'
style={{ width: '200px' }}
{...getFieldProps('select', {
rules: [
{
required: true,
},
],
})}
>
<Option value='1'>1</Option>
<Option value='2'>2</Option>
</Select></div>
<div style={errorStyle}>
{(errors) ? errors.join(',') : null}
</div>
<div style={errorStyle}>
{isFieldValidating('select') ? 'validating' : null}
</div>
</div>)
},
}
const DateInput = {
props: {
form: Object,
},
render () {
const { getFieldProps, getFieldError } = this.form
const errors = getFieldError('date')
return (<div style={ regionStyle }>
<div><span style={{ color: 'red' }}>*</span> DateInput sync validate</div>
<div style={{ width: '200px' }}>
<DatePicker
placeholder='please select'
{...getFieldProps('date', {
rules: [
{
required: true,
type: 'object',
},
],
})}
/>
</div>
<div style={errorStyle}>
{(errors) ? errors.join(',') : null}
</div>
</div>)
},
}
function toNumber (v) {
if (v === undefined) {
return v
}
if (v === '') {
return undefined
}
if (v && v.trim() === '') {
return NaN
}
return Number(v)
}
const NumberInput = {
props: {
form: Object,
},
render () {
const { getFieldProps, getFieldError } = this.form
const errors = getFieldError('number')
return (<div style={ regionStyle }>
<div>number input</div>
<div>
<input
{...getFieldProps('number', {
initialValue: '1',
rules: [{
transform: toNumber,
type: 'number',
}],
})}
/>
</div>
<div style={errorStyle}>
{(errors) ? errors.join(',') : null}
</div>
</div>)
},
}
const Form = {
methods: {
onSubmit (e) {
console.log('submit')
e.preventDefault()
this.form.validateFieldsAndScroll({ scroll: { offsetTop: 20 }}, (error, values) => {
if (!error) {
console.log('ok', values)
} else {
console.log('error', error, values)
}
})
},
reset (e) {
e.preventDefault()
this.form.resetFields()
},
},
render () {
const { form } = this
const { getFieldProps, getFieldError } = form
return (<div style={{ margin: '20px' }}>
<h2>overview</h2>
<form onSubmit={this.onSubmit}>
<User form={ form } saveRef={this.saveRef}/>
<NumberInput form={ form }/>
<Email form={ form }/>
<CustomInput form={ form }/>
<DateInput form={ form }/>
<div style={ regionStyle }>
<div>normal required input</div>
<div>
<input
{...getFieldProps('normal', {
initialValue: '',
rules: [{
required: true,
}],
})}
/>
</div>
<div style={errorStyle}>
{(getFieldError('normal')) ? getFieldError('normal').join(',') : null}
</div>
</div>
<div style={ regionStyle }>
<button onClick={this.reset}>reset</button>
&nbsp;
<input type='submit' value='submit'/>
</div>
</form>
</div>)
},
}
export default createDOMForm({
validateMessages: {
required (field) {
return `${field} 必填`
},
},
})(Form)

View File

@ -20,7 +20,7 @@ import {
flattenArray,
} from './utils'
const DEFAULT_TRIGGER = 'input'
const DEFAULT_TRIGGER = 'change'
function createBaseForm (option = {}, mixins = []) {
const {
@ -534,15 +534,15 @@ function createBaseForm (option = {}, mixins = []) {
},
render () {
const { $props, $listeners } = this
const { $listeners } = this
const formProps = {
[formPropName]: this.getForm(),
}
const props = getOptionProps(this)
const wrappedComponentProps = {
props: mapProps.call(this, {
...formProps,
...$props,
...props,
}),
on: $listeners,
}
@ -552,7 +552,12 @@ function createBaseForm (option = {}, mixins = []) {
return <WrappedComponent {...wrappedComponentProps}/>
},
}
if (!(WrappedComponent.props && formPropName in WrappedComponent.props)) {
WrappedComponent.props = {
...WrappedComponent.props,
[formPropName]: Object,
}
}
return argumentContainer(Form, WrappedComponent)
}
}

View File

@ -52,7 +52,7 @@ const mixin = {
methods: {
getForm () {
return {
...formMixin.getForm.call(this),
...formMixin.methods.getForm.call(this),
validateFieldsAndScroll: this.validateFieldsAndScroll,
}
},
@ -69,7 +69,7 @@ const mixin = {
if (has(error, name)) {
const instance = this.getFieldInstance(name)
if (instance) {
const node = instance.$el
const node = instance.$el || instance.elm
const top = node.getBoundingClientRect().top
if (firstTop === undefined || firstTop > top) {
firstTop = top

View File

@ -3,14 +3,7 @@ import createForm from './createForm'
import createFormField from './createFormField'
import formShape from './propTypes'
import Vue from 'vue'
Vue.directive('ant-ref', {
bind: function (el, binding, vnode) {
binding.value(vnode)
},
unbind: function (el, binding, vnode) {
binding.value()
},
})
import antRefDirective from '../../_util/antRefDirective'
Vue.use(antRefDirective)
export { createForm, createFormField, formShape }

View File

@ -6,7 +6,7 @@ export function argumentContainer (Container, WrappedComponent) {
/* eslint no-param-reassign:0 */
Container.name = `Form_${getDisplayName(WrappedComponent)}`
Container.WrappedComponent = WrappedComponent
Container.methods = { ...Container.methods, ...WrappedComponent.methods }
Container.props = { ...Container.props, ...WrappedComponent.props }
return Container
}

View File

@ -7,6 +7,9 @@ import createChainedFunction from '../_util/createChainedFunction'
import getTransitionProps from '../_util/getTransitionProps'
import Notice from './Notice'
import antRefDirective from '../_util/antRefDirective'
Vue.use(antRefDirective)
let seed = 0
const now = Date.now()