2018-05-02 13:35:42 +00:00
|
|
|
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 = {
|
2018-05-03 11:10:43 +00:00
|
|
|
methods: {
|
|
|
|
getForm () {
|
|
|
|
return {
|
2018-05-04 08:02:31 +00:00
|
|
|
...formMixin.methods.getForm.call(this),
|
2018-05-03 11:10:43 +00:00
|
|
|
validateFieldsAndScroll: this.validateFieldsAndScroll,
|
|
|
|
}
|
|
|
|
},
|
2018-05-02 13:35:42 +00:00
|
|
|
|
2018-05-03 11:10:43 +00:00
|
|
|
validateFieldsAndScroll (ns, opt, cb) {
|
|
|
|
const { names, callback, options } = getParams(ns, opt, cb)
|
2018-05-02 13:35:42 +00:00
|
|
|
|
2018-05-03 11:10:43 +00:00
|
|
|
const newCb = (error, values) => {
|
|
|
|
if (error) {
|
|
|
|
const validNames = this.fieldsStore.getValidFieldsName()
|
|
|
|
let firstNode
|
|
|
|
let firstTop
|
2018-10-31 13:39:12 +00:00
|
|
|
validNames.forEach((name) => {
|
2018-05-03 11:10:43 +00:00
|
|
|
if (has(error, name)) {
|
|
|
|
const instance = this.getFieldInstance(name)
|
|
|
|
if (instance) {
|
2018-05-04 08:02:31 +00:00
|
|
|
const node = instance.$el || instance.elm
|
2018-05-03 11:10:43 +00:00
|
|
|
const top = node.getBoundingClientRect().top
|
2018-09-05 13:28:54 +00:00
|
|
|
if (node.type !== 'hidden' && (firstTop === undefined || firstTop > top)) {
|
2018-05-03 11:10:43 +00:00
|
|
|
firstTop = top
|
|
|
|
firstNode = node
|
|
|
|
}
|
2018-05-02 13:35:42 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-31 13:39:12 +00:00
|
|
|
})
|
|
|
|
|
2018-05-03 11:10:43 +00:00
|
|
|
if (firstNode) {
|
|
|
|
const c = options.container || getScrollableContainer(firstNode)
|
|
|
|
scrollIntoView(firstNode, c, {
|
|
|
|
onlyScrollIfNeeded: true,
|
|
|
|
...options.scroll,
|
|
|
|
})
|
|
|
|
}
|
2018-05-02 13:35:42 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 11:10:43 +00:00
|
|
|
if (typeof callback === 'function') {
|
|
|
|
callback(error, values)
|
|
|
|
}
|
2018-05-02 13:35:42 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 11:10:43 +00:00
|
|
|
return this.validateFields(names, options, newCb)
|
|
|
|
},
|
2018-05-02 13:35:42 +00:00
|
|
|
},
|
2018-05-03 11:10:43 +00:00
|
|
|
|
2018-05-02 13:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function createDOMForm (option) {
|
|
|
|
return createBaseForm({
|
|
|
|
...option,
|
|
|
|
}, [mixin])
|
|
|
|
}
|
|
|
|
|
|
|
|
export default createDOMForm
|