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 = { methods: { getForm () { return { ...formMixin.methods.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 || instance.elm 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