1
0
mirror of https://gitlab.com/bramw/baserow.git synced 2024-11-21 23:37:55 +00:00
bramw_baserow/web-frontend/modules/core/directives/autoScroll.js

117 lines
3.9 KiB
JavaScript

/**
* A directive that helps with auto scrolling when the mouse pointer reaches the
* edge of the element.
*
* v-auto-scroll="{
* // Indicates whether we should scroll vertically or horizontally by providing
* // `vertical` or `horizontal`.
* orientation: 'vertical'
* // Dynamic indication if the auto scrolling is enabled. This is typically used
* // we should only auto scroll if dragging a certain element.
* enabled: () => true,
* // The speed that should be scrolled with.
* speed: 3,
* // Indicates the percentage of the edges that should trigger the auto
* // scrolling. If 10 is provided, then the auto scrolling starts when the mouse
* // pointer is in 10% of the edge of the element.
* padding: 10,
* // The element that should be scrolled in. By default this is the element
* // that's binding by the directive, but it can optionally be changed.
* scrollElement: () => DOMElement
* }"
*/
export default {
bind(el, binding) {
binding.def.update(el, binding)
el.autoScrollTimeout = null
el.autoScrollLastMoveEvent = null
const autoscrollLoop = () => {
if (!el.autoScrollConfig.enabled()) {
clearTimeout(el.autoScrollTimeout)
el.autoScrollTimeout = null
return
}
const scrollElement = el.autoScrollConfig.scrollElement()
const rect = scrollElement.getBoundingClientRect()
let size
let autoScrollMouseStart
if (el.autoScrollConfig.orientation === 'horizontal') {
size = rect.right - rect.left
autoScrollMouseStart = el.autoScrollLastMoveEvent.clientX - rect.left
} else {
size = rect.bottom - rect.top
autoScrollMouseStart = el.autoScrollLastMoveEvent.clientY - rect.top
}
const autoScrollMouseEnd = size - autoScrollMouseStart
const side = Math.ceil((size / 100) * el.autoScrollConfig.padding)
let speed = 0
if (autoScrollMouseStart < side) {
speed = -(
el.autoScrollConfig.speed -
Math.ceil(
(Math.max(0, autoScrollMouseStart) / side) *
el.autoScrollConfig.speed
)
)
} else if (autoScrollMouseEnd < side) {
speed =
el.autoScrollConfig.speed -
Math.ceil(
(Math.max(0, autoScrollMouseEnd) / side) * el.autoScrollConfig.speed
)
}
// If the speed is either a positive or negative, so not 0, we know that we
// need to start auto scrolling.
if (speed !== 0) {
// Only update the element if the `onScroll` functions returns `true`. This
// is because in some cases, scrolling is handled in another way. This is
// for example the case with the `GridView`.
if (el.autoScrollConfig.onScroll(speed)) {
if (el.autoScrollConfig.orientation === 'horizontal') {
scrollElement.scrollLeft += speed
} else {
scrollElement.scrollTop += speed
}
}
el.autoScrollTimeout = setTimeout(() => {
autoscrollLoop()
}, 2)
} else {
clearTimeout(el.autoScrollTimeout)
el.autoScrollTimeout = null
}
}
el.autoScrollMouseMoveEvent = (event) => {
el.autoScrollLastMoveEvent = event
if (el.autoScrollTimeout === null) {
autoscrollLoop()
}
}
el.addEventListener('mousemove', el.autoScrollMouseMoveEvent)
},
update(el, binding) {
const defaultEnabled = () => true
const defaultScrollElement = () => el
const defaultOnScroll = () => true
el.autoScrollConfig = {
orientation: binding.value.orientation || 'vertical',
enabled: binding.value.enabled || defaultEnabled,
speed: binding.value.speed || 3,
padding: binding.value.padding || 10,
scrollElement: binding.value.scrollElement || defaultScrollElement,
onScroll: binding.value.onScroll || defaultOnScroll,
}
},
unbind(el) {
el.removeEventListener('mousemove', el.autoScrollMouseMoveEvent)
},
}