Skip to content

Auto Scroll

Adds horizontal and vertical auto-scrolling for the closest scrollable container element.

Demo

ts
export interface AutoScrollMiddlewareOptions {
  /**
   * Interval (in milliseconds)
   *
   * Defaults to: 8
   */
  interval: number
  /**
   * Steps (in pixels)
   *
   * Defaults to: 4
   */
  steps: number
  /**
   * Distance to the outer bounds of the scrollable container that triggers
   * auto-scrolling.
   *
   * Defaults to: 100
   */
  threshold: number
}
vue
<script lang="ts" setup>
import createDragDropObservable, {
  addClasses,
  autoScroll,
  dragImage,
  indicator,
  reorderItems,
} from 'dndrxjs'
import { onMounted, onUnmounted, ref } from 'vue'
import COLORS from './data/MOCK_DATA_COLORS.json'

const items = ref(COLORS.map(hex => ({ id: hex })))
const container = ref(null)

onMounted(() => {
  const subscription = createDragDropObservable({
    container: container.value!,
    vertical: false,
    dropPositionFn: ({ dragElement, dropElement }) => 'around',
  })
    .pipe(
      addClasses(),
      indicator({ offset: 6 }),
      dragImage({ minElements: 1 }),
      autoScroll({ threshold: 120 }), 
    )
    .subscribe(({ type, dragElements, dropElement, position }) => {
      if (!!dropElement && type === 'DragEnd') {
        const index = Number.parseInt(dropElement?.getAttribute('data-index') || '0')
        const selectedItems = dragElements.map(
          e =>
            items.value.find(item => item.id === e.getAttribute('data-id'))!,
        )
        if (position === 'after') {
          items.value = reorderItems(items.value, selectedItems, index + 1)
        }
        else if (position === 'before') {
          items.value = reorderItems(items.value, selectedItems, index)
        }
      }
    })

  onUnmounted(() => subscription.unsubscribe())
})
</script>

<template>
  <div
    ref="container"
    class="demo"
    style="
      overflow: scroll;
      max-height: 420px;
      padding: 12px;
      position: relative;
    "
  >
    <div
      style="
        display: flex;
        flex-wrap: wrap;
        gap: 12px;
        width: calc((180px + 12px) * 10 - 12px);
      "
    >
      <div
        v-for="(item, index) in items"
        :key="item.id"
        draggable="false"
        :data-index="index"
        :data-id="item.id"
      >
        <div
          style="
            width: 180px;
            height: 180px;
            padding: 5px;
            font-size: 13px;
            border-radius: 4px;
            display: flex;
            text-align: center;
            align-items: center;
            justify-content: center;
            font-weight: bold;
            color: #fff;
          "
          :style="{ background: item.id }"
        >
          <span>{{ item.id }}</span>
        </div>
      </div>
    </div>
  </div>
</template>