Add automatic scrolling left/right when dragging to reorder columns

This commit is contained in:
Andrew Kingston 2023-05-16 19:48:14 +01:00
parent ba9691ee12
commit cadd1b5a4e
1 changed files with 77 additions and 9 deletions

View File

@ -4,9 +4,10 @@ const reorderInitialState = {
sourceColumn: null, sourceColumn: null,
targetColumn: null, targetColumn: null,
breakpoints: [], breakpoints: [],
initialMouseX: null,
scrollLeft: 0,
gridLeft: 0, gridLeft: 0,
width: 0,
latestX: 0,
increment: 0,
} }
export const createStores = () => { export const createStores = () => {
@ -23,14 +24,24 @@ export const createStores = () => {
} }
export const deriveStores = context => { export const deriveStores = context => {
const { reorder, columns, visibleColumns, scroll, bounds, stickyColumn, ui } = const {
context reorder,
columns,
visibleColumns,
scroll,
bounds,
stickyColumn,
ui,
maxScrollLeft,
} = context
let autoScrollInterval
let isAutoScrolling
// Callback when dragging on a colum header and starting reordering // Callback when dragging on a colum header and starting reordering
const startReordering = (column, e) => { const startReordering = (column, e) => {
const $visibleColumns = get(visibleColumns) const $visibleColumns = get(visibleColumns)
const $bounds = get(bounds) const $bounds = get(bounds)
const $scroll = get(scroll)
const $stickyColumn = get(stickyColumn) const $stickyColumn = get(stickyColumn)
ui.actions.blur() ui.actions.blur()
@ -51,9 +62,8 @@ export const deriveStores = context => {
sourceColumn: column, sourceColumn: column,
targetColumn: null, targetColumn: null,
breakpoints, breakpoints,
initialMouseX: e.clientX,
scrollLeft: $scroll.left,
gridLeft: $bounds.left, gridLeft: $bounds.left,
width: $bounds.width,
}) })
// Add listeners to handle mouse movement // Add listeners to handle mouse movement
@ -66,12 +76,44 @@ export const deriveStores = context => {
// Callback when moving the mouse when reordering columns // Callback when moving the mouse when reordering columns
const onReorderMouseMove = e => { const onReorderMouseMove = e => {
// Immediately handle the current position
const x = e.clientX
reorder.update(state => ({
...state,
latestX: x,
}))
considerReorderPosition()
// Check if we need to start auto-scrolling
const $reorder = get(reorder) const $reorder = get(reorder)
const proximityCutoff = 140
const speedFactor = 8
const rightProximity = Math.max(0, $reorder.gridLeft + $reorder.width - x)
const leftProximity = Math.max(0, x - $reorder.gridLeft)
if (rightProximity < proximityCutoff) {
const weight = proximityCutoff - rightProximity
const increment = (weight / proximityCutoff) * speedFactor
reorder.update(state => ({ ...state, increment }))
startAutoScroll()
} else if (leftProximity < proximityCutoff) {
const weight = -1 * (proximityCutoff - leftProximity)
const increment = (weight / proximityCutoff) * speedFactor
reorder.update(state => ({ ...state, increment }))
startAutoScroll()
} else {
stopAutoScroll()
}
}
// Actual logic to consider the current position and determine the new order
const considerReorderPosition = () => {
const $reorder = get(reorder)
const $scroll = get(scroll)
// Compute the closest breakpoint to the current position // Compute the closest breakpoint to the current position
let targetColumn let targetColumn
let minDistance = Number.MAX_SAFE_INTEGER let minDistance = Number.MAX_SAFE_INTEGER
const mouseX = e.clientX - $reorder.gridLeft + $reorder.scrollLeft const mouseX = $reorder.latestX - $reorder.gridLeft + $scroll.left
$reorder.breakpoints.forEach(point => { $reorder.breakpoints.forEach(point => {
const distance = Math.abs(point.x - mouseX) const distance = Math.abs(point.x - mouseX)
if (distance < minDistance) { if (distance < minDistance) {
@ -79,7 +121,6 @@ export const deriveStores = context => {
targetColumn = point.column targetColumn = point.column
} }
}) })
if (targetColumn !== $reorder.targetColumn) { if (targetColumn !== $reorder.targetColumn) {
reorder.update(state => ({ reorder.update(state => ({
...state, ...state,
@ -88,8 +129,35 @@ export const deriveStores = context => {
} }
} }
// Commences auto-scrolling in a certain direction, triggered when the mouse
// approaches the edges of the grid
const startAutoScroll = () => {
if (isAutoScrolling) {
return
}
isAutoScrolling = true
autoScrollInterval = setInterval(() => {
const $maxLeft = get(maxScrollLeft)
const { increment } = get(reorder)
scroll.update(state => ({
...state,
left: Math.max(0, Math.min($maxLeft, state.left + increment)),
}))
considerReorderPosition()
}, 10)
}
// Stops auto scrolling
const stopAutoScroll = () => {
isAutoScrolling = false
clearInterval(autoScrollInterval)
}
// Callback when stopping reordering columns // Callback when stopping reordering columns
const stopReordering = async () => { const stopReordering = async () => {
// Ensure auto-scrolling is stopped
stopAutoScroll()
// Swap position of columns // Swap position of columns
let { sourceColumn, targetColumn } = get(reorder) let { sourceColumn, targetColumn } = get(reorder)
moveColumn(sourceColumn, targetColumn) moveColumn(sourceColumn, targetColumn)