105 lines
2.8 KiB
React
105 lines
2.8 KiB
React
![]() |
import scrollIntoView from 'dom-scroll-into-view'
|
||
|
import has from 'lodash/has'
|
||
|
import createBaseForm from './createBaseForm'
|
||
|
import { mixin as formMixin } from './createForm'
|
||
|
import { getParams } from './utils'
|
||
|
|
||
|
function computedStyle (el, prop) {
|
||
|
const getComputedStyle = window.getComputedStyle
|
||
|
const style =
|
||
|
// If we have getComputedStyle
|
||
|
getComputedStyle
|
||
|
// Query it
|
||
|
// TODO: From CSS-Query notes, we might need (node, null) for FF
|
||
|
? getComputedStyle(el)
|
||
|
|
||
|
// Otherwise, we are in IE and use currentStyle
|
||
|
: el.currentStyle
|
||
|
if (style) {
|
||
|
return style[
|
||
|
// Switch to camelCase for CSSOM
|
||
|
// DEV: Grabbed from jQuery
|
||
|
// https://github.com/jquery/jquery/blob/1.9-stable/src/css.js#L191-L194
|
||
|
// https://github.com/jquery/jquery/blob/1.9-stable/src/core.js#L593-L597
|
||
|
prop.replace(/-(\w)/gi, (word, letter) => {
|
||
|
return letter.toUpperCase()
|
||
|
})
|
||
|
]
|
||
|
}
|
||
|
return undefined
|
||
|
}
|
||
|
|
||
|
function getScrollableContainer (n) {
|
||
|
let node = n
|
||
|
let nodeName
|
||
|
/* eslint no-cond-assign:0 */
|
||
|
while ((nodeName = node.nodeName.toLowerCase()) !== 'body') {
|
||
|
const overflowY = computedStyle(node, 'overflowY')
|
||
|
// https://stackoverflow.com/a/36900407/3040605
|
||
|
if (
|
||
|
node !== n &&
|
||
|
(overflowY === 'auto' || overflowY === 'scroll') &&
|
||
|
node.scrollHeight > node.clientHeight
|
||
|
) {
|
||
|
return node
|
||
|
}
|
||
|
node = node.parentNode
|
||
|
}
|
||
|
return nodeName === 'body' ? node.ownerDocument : node
|
||
|
}
|
||
|
|
||
|
const mixin = {
|
||
|
getForm () {
|
||
|
return {
|
||
|
...formMixin.getForm.call(this),
|
||
|
validateFieldsAndScroll: this.validateFieldsAndScroll,
|
||
|
}
|
||
|
},
|
||
|
|
||
|
validateFieldsAndScroll (ns, opt, cb) {
|
||
|
const { names, callback, options } = getParams(ns, opt, cb)
|
||
|
|
||
|
const newCb = (error, values) => {
|
||
|
if (error) {
|
||
|
const validNames = this.fieldsStore.getValidFieldsName()
|
||
|
let firstNode
|
||
|
let firstTop
|
||
|
for (const name of validNames) {
|
||
|
if (has(error, name)) {
|
||
|
const instance = this.getFieldInstance(name)
|
||
|
if (instance) {
|
||
|
const node = instance.$el
|
||
|
const top = node.getBoundingClientRect().top
|
||
|
if (firstTop === undefined || firstTop > top) {
|
||
|
firstTop = top
|
||
|
firstNode = node
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (firstNode) {
|
||
|
const c = options.container || getScrollableContainer(firstNode)
|
||
|
scrollIntoView(firstNode, c, {
|
||
|
onlyScrollIfNeeded: true,
|
||
|
...options.scroll,
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (typeof callback === 'function') {
|
||
|
callback(error, values)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return this.validateFields(names, options, newCb)
|
||
|
},
|
||
|
}
|
||
|
|
||
|
function createDOMForm (option) {
|
||
|
return createBaseForm({
|
||
|
...option,
|
||
|
}, [mixin])
|
||
|
}
|
||
|
|
||
|
export default createDOMForm
|