taye/interact.js

View on GitHub
packages/@interactjs/snappers/grid.ts

Summary

Maintainability
A
0 mins
Test Coverage
import type { Rect, Point } from '@interactjs/core/types'
import type { SnapFunction, SnapTarget } from '@interactjs/modifiers/snap/pointer'

export interface GridOptionsBase {
  range?: number
  limits?: Rect
  offset?: Point
}
export interface GridOptionsXY extends GridOptionsBase {
  x: number
  y: number
}
export interface GridOptionsTopLeft extends GridOptionsBase {
  top?: number
  left?: number
}
export interface GridOptionsBottomRight extends GridOptionsBase {
  bottom?: number
  right?: number
}
export interface GridOptionsWidthHeight extends GridOptionsBase {
  width?: number
  height?: number
}

export type GridOptions = GridOptionsXY | GridOptionsTopLeft | GridOptionsBottomRight | GridOptionsWidthHeight

export default (grid: GridOptions) => {
  const coordFields = (
    [
      ['x', 'y'],
      ['left', 'top'],
      ['right', 'bottom'],
      ['width', 'height'],
    ] as const
  ).filter(([xField, yField]) => xField in grid || yField in grid)

  const gridFunc: SnapFunction & {
    grid: typeof grid
    coordFields: typeof coordFields
  } = (x, y) => {
    const {
      range,
      limits = {
        left: -Infinity,
        right: Infinity,
        top: -Infinity,
        bottom: Infinity,
      },
      offset = { x: 0, y: 0 },
    } = grid

    const result: SnapTarget & {
      grid: typeof grid
    } = { range, grid, x: null as number, y: null as number }

    for (const [xField, yField] of coordFields) {
      const gridx = Math.round((x - offset.x) / (grid as any)[xField])
      const gridy = Math.round((y - offset.y) / (grid as any)[yField])

      result[xField] = Math.max(limits.left, Math.min(limits.right, gridx * (grid as any)[xField] + offset.x))
      result[yField] = Math.max(limits.top, Math.min(limits.bottom, gridy * (grid as any)[yField] + offset.y))
    }

    return result
  }

  gridFunc.grid = grid
  gridFunc.coordFields = coordFields

  return gridFunc
}