From be98a04520c8c150f4896d20ab88f7496c73f987 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Fri, 4 May 2018 16:02:31 +0800 Subject: [PATCH] feat: add vc-form demo --- components/_util/ContainerRender.jsx | 2 + components/_util/antRefDirective.js | 12 + components/modal/confirm.js | 4 + components/trigger/Trigger.jsx | 2 + components/vc-form/demo/modal.js | 84 +++++++ components/vc-form/demo/nested-field.js | 194 +++++++++++++++ components/vc-form/demo/normalize.js | 151 ++++++++++++ components/vc-form/demo/overview.js | 259 ++++++++++++++++++++ components/vc-form/src/createBaseForm.jsx | 15 +- components/vc-form/src/createDOMForm.jsx | 4 +- components/vc-form/src/index.jsx | 11 +- components/vc-form/src/utils.js | 2 +- components/vc-notification/Notification.jsx | 3 + 13 files changed, 726 insertions(+), 17 deletions(-) create mode 100644 components/_util/antRefDirective.js create mode 100644 components/vc-form/demo/modal.js create mode 100644 components/vc-form/demo/nested-field.js create mode 100644 components/vc-form/demo/normalize.js create mode 100644 components/vc-form/demo/overview.js diff --git a/components/_util/ContainerRender.jsx b/components/_util/ContainerRender.jsx index 9540e6db4..523922b95 100644 --- a/components/_util/ContainerRender.jsx +++ b/components/_util/ContainerRender.jsx @@ -1,6 +1,8 @@ import Vue from 'vue' import PropTypes from './vue-types' +import antRefDirective from './antRefDirective' +Vue.use(antRefDirective) export default { props: { diff --git a/components/_util/antRefDirective.js b/components/_util/antRefDirective.js new file mode 100644 index 000000000..c7d0afde4 --- /dev/null +++ b/components/_util/antRefDirective.js @@ -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() + }, + }) + }, +} diff --git a/components/modal/confirm.js b/components/modal/confirm.js index 69ac8082d..fc2f7913a 100644 --- a/components/modal/confirm.js +++ b/components/modal/confirm.js @@ -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') diff --git a/components/trigger/Trigger.jsx b/components/trigger/Trigger.jsx index 547411e7f..731b27425 100644 --- a/components/trigger/Trigger.jsx +++ b/components/trigger/Trigger.jsx @@ -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 '' diff --git a/components/vc-form/demo/modal.js b/components/vc-form/demo/modal.js new file mode 100644 index 000000000..a9d127886 --- /dev/null +++ b/components/vc-form/demo/modal.js @@ -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 (
+

modal

+ +
+
+ +
+ {getFieldError('required') ? getFieldError('required').join(',') + : 1} +
+
+ +
+
+
+
+
+ +
+
) + }, +} + +export default createDOMForm()(Form) diff --git a/components/vc-form/demo/nested-field.js b/components/vc-form/demo/nested-field.js new file mode 100644 index 000000000..a04bdb271 --- /dev/null +++ b/components/vc-form/demo/nested-field.js @@ -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 ( +
+
Member 0 firstname
+ {getFieldDecorator('member[0].name.firstname', { + initialValue: '', + rules: [{ + required: true, + message: 'What\'s the member_0 firstname?', + }], + })( + + )} +
+ {(getFieldError('member[0].name.firstname') || []).join(', ')} +
+ +
Member 0 lastname
+ {getFieldDecorator('member[0].name.lastname', { + initialValue: '', + rules: [{ + required: true, + message: 'What\'s the member_0 lastname?', + }], + })( + + )} +
+ {(getFieldError('member[0].name.firstname') || []).join(', ')} +
+ +
Member 1 firstname
+ {getFieldDecorator('member[1].name.firstname', { + initialValue: '', + rules: [{ + required: true, + message: 'What\'s the member_1 fistname?', + }], + })( + + )} +
+ {(getFieldError('member[1].name.firstname') || []).join(', ')} +
+ +
Member 1 lastname
+ {getFieldDecorator('member[1].name.lastname', { + initialValue: '', + rules: [{ + required: true, + message: 'What\'s the member_1 lastname?', + }], + })( + + )} +
+ {(getFieldError('member[1].name.firstname') || []).join(', ')} +
+ +
a[0][1].b.c[0]
+ {getFieldDecorator('a[0][1].b.c[0]', { + initialValue: '', + rules: [{ + required: true, + message: 'What\'s a[0][1].b.c[0]?', + }], + })( + + )} +
+ {(getFieldError('a[0][1].b.c[0]') || []).join(', ')} +
+ +
w.x.y.z[0]
+ {getFieldDecorator('w.x.y.z[0]', { + initialValue: '', + rules: [{ + required: true, + message: 'What\'s w.x.y.z[0]?', + }], + })( + + )} +
+ {(getFieldError('w.x.y.z[0]') || []).join(', ')} +
+ + + + +
+ ) + }, +} + +const NewForm = createForm({ + onFieldsChange (_, changedFields, allFields) { + console.log('onFieldsChange: ', changedFields, allFields) + }, + onValuesChange (_, changedValues, allValues) { + console.log('onValuesChange: ', changedValues, allValues) + }, +})(Form) + +export default { + render () { + return (
+

setFieldsValue

+ +
) + }, +} diff --git a/components/vc-form/demo/normalize.js b/components/vc-form/demo/normalize.js new file mode 100644 index 000000000..4de51e37f --- /dev/null +++ b/components/vc-form/demo/normalize.js @@ -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 (
+
upper normalize
+
+ +
+
+ {(errors) ? errors.join(',') : null} +
+
) + }, +} + +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 (
+
min: +
+
max: +
+
) + }, +} + +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 (
+

normalize

+
+ + + + +
+ +
+ +
) + }, +} + +export default createForm()(Form) + diff --git a/components/vc-form/demo/overview.js b/components/vc-form/demo/overview.js new file mode 100644 index 000000000..b4785c2a8 --- /dev/null +++ b/components/vc-form/demo/overview.js @@ -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 (
+
email sync validate
+
+ 错误的 email 格式, + }, + ], + })} + />
+
+ {errors} +
+
+ {isFieldValidating('email') ? 'validating' : null} +
+
) + }, + +} + +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 (
+
* user async validate
+
+
+
+ {(errors) ? errors.join(',') : null} +
+
+ {isFieldValidating('user') ? 'validating' : null} +
+
) + }, +} + +const CustomInput = { + props: { + form: Object, + }, + render () { + const { getFieldProps, getFieldError, isFieldValidating } = this.form + const errors = getFieldError('select') + return (
+
* custom select sync validate
+
+
+ {(errors) ? errors.join(',') : null} +
+
+ {isFieldValidating('select') ? 'validating' : null} +
+
) + }, + +} + +const DateInput = { + props: { + form: Object, + }, + render () { + const { getFieldProps, getFieldError } = this.form + const errors = getFieldError('date') + return (
+
* DateInput sync validate
+
+ +
+
+ {(errors) ? errors.join(',') : null} +
+
) + }, +} + +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 (
+
number input
+
+ +
+
+ {(errors) ? errors.join(',') : null} +
+
) + }, + +} + +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 (
+

overview

+
+ + + + + + + + + + +
+
normal required input
+
+ +
+
+ {(getFieldError('normal')) ? getFieldError('normal').join(',') : null} +
+
+ +
+ +   + +
+ +
) + }, +} + +export default createDOMForm({ + validateMessages: { + required (field) { + return `${field} 必填` + }, + }, +})(Form) diff --git a/components/vc-form/src/createBaseForm.jsx b/components/vc-form/src/createBaseForm.jsx index 07a85baf7..dd482bf80 100644 --- a/components/vc-form/src/createBaseForm.jsx +++ b/components/vc-form/src/createBaseForm.jsx @@ -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 }, } - + if (!(WrappedComponent.props && formPropName in WrappedComponent.props)) { + WrappedComponent.props = { + ...WrappedComponent.props, + [formPropName]: Object, + } + } return argumentContainer(Form, WrappedComponent) } } diff --git a/components/vc-form/src/createDOMForm.jsx b/components/vc-form/src/createDOMForm.jsx index 9fdc5dc12..ed4393017 100644 --- a/components/vc-form/src/createDOMForm.jsx +++ b/components/vc-form/src/createDOMForm.jsx @@ -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 diff --git a/components/vc-form/src/index.jsx b/components/vc-form/src/index.jsx index 4d3bdf6d8..344a848c6 100644 --- a/components/vc-form/src/index.jsx +++ b/components/vc-form/src/index.jsx @@ -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 } diff --git a/components/vc-form/src/utils.js b/components/vc-form/src/utils.js index 7819380bc..626cae283 100644 --- a/components/vc-form/src/utils.js +++ b/components/vc-form/src/utils.js @@ -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 } diff --git a/components/vc-notification/Notification.jsx b/components/vc-notification/Notification.jsx index 81088375f..37199baff 100644 --- a/components/vc-notification/Notification.jsx +++ b/components/vc-notification/Notification.jsx @@ -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()