npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dynamic-gridline

v1.0.5

Published

A draggable and zoomable grid system for placing items.

Readme

Contributors Forks Stargazers Issues project_license LinkedIn

About The Project

Demo Dynamic Gridline

Built With

  • React
  • Motion

Getting Started

Prerequisites

# npm
npm install motion @radix-ui/react-slider

# yarn
yarn add motion @radix-ui/react-slider

# pnpm
pnpm add motion @radix-ui/react-slider

# bun
bun install motion @radix-ui/react-slider

Installation

# npm
npm install dynamic-gridline

# yarn
yarn add dynamic-gridline

# pnpm
pnpm add dynamic-gridline

# bun
bun install dynamic-gridline

Usage

Basic Setup

Import the components and start building your interactive grid:

import { Grid, GridItem } from 'dynamic-gridline'

function App() {
  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <Grid>
        <GridItem x={100} y={100}>
          <div>Your content here</div>
        </GridItem>
      </Grid>
    </div>
  )
}

Core Components

Grid Component

The Grid component is the main container that provides the interactive canvas with pan, zoom, and grid functionality.

import { Grid } from 'dynamic-gridline'

function InteractiveCanvas() {
  return (
    <Grid
      config={{
        width: 10000,
        height: 8000,
        gridCellSize: 50,
        minZoom: 0.1,
        maxZoom: 2,
        gridColor: '#e0e0e0',
      }}
    >
      {/* Your grid items go here */}
    </Grid>
  )
}

GridItem Component

The GridItem component positions content within the grid coordinate system.

import { Grid, GridItem } from 'dynamic-gridline'

function ItemPlacement() {
  return (
    <Grid>
      <GridItem x={0} y={0}>
        <div style={{ background: 'red', text: 'white', padding: '10px' }}>(0,0)</div>
      </GridItem>
      <GridItem x={200} y={150}>
        <div style={{ background: 'blue', text: 'white', padding: '10px' }}>(200,150)</div>
      </GridItem>
      <GridItem x={-150} y={-50}>
        <div style={{ background: 'green', text: 'white', padding: '10px' }}>(-150, -50)</div>
      </GridItem>
    </Grid>
  )
}

Advanced Features

Disable Scale for Grid Items

Control whether items scale with zoom using the disableScale property:

function ScalingDemo() {
  return (
    <Grid config={{ minZoom: 0.5, maxZoom: 2 }}>
      {/* This circle will scale with zoom */}
      <GridItem x={-100} y={0} disableScale={false}>
        <div
          style={{
            width: 120,
            height: 120,
            color: 'white',
            borderRadius: '50%',
            background: 'blue',
          }}
        ></div>
      </GridItem>

      {/* This circle maintains constant size */}
      <GridItem x={100} y={0} disableScale={true}>
        <div
          style={{
            width: 120,
            height: 120,
            color: 'white',
            borderRadius: '50%',
            background: 'red',
          }}
        ></div>
      </GridItem>
    </Grid>
  )
}

Fixed Z-Index Control

Control layering of grid items with fixedZIndex:

function LayeringDemo() {
  return (
    <Grid>
      <GridItem x={-50} y={-50} fixedZIndex={1}>
        <div
          style={{
            width: 100,
            height: 100,
            background: 'rgba(255,0,0,0.7)',
            color: 'white',
          }}
        >
          Behind (z-index: 1)
        </div>
      </GridItem>

      <GridItem x={0} y={0} fixedZIndex={10}>
        <div
          style={{
            width: 100,
            height: 100,
            background: 'rgba(0,0,255,0.7)',
            color: 'white',
          }}
        >
          In Front (z-index: 10)
        </div>
      </GridItem>
    </Grid>
  )
}

Interaction Features

Click Event Handlers

Handle fast clicks and hold clicks on the grid:

function ClickHandling() {
  const handleFastClick = ({ x, y }: { x: number; y: number }) => {
    console.log('Fast click at:', x, y)
    // Add logic for quick interactions
  }

  const handleHoldClick = ({ x, y }: { x: number; y: number }) => {
    console.log('Hold click at:', x, y)
    // Add logic for context menus or long-press actions
  }

  return (
    <Grid
      config={{
        onFastClick: handleFastClick,
        onHoldClick: handleHoldClick,
      }}
    >
      <GridItem x={0} y={0}>
        <div>Try clicking and holding on the grid!</div>
      </GridItem>
    </Grid>
  )
}

Mouse Movement Tracking

Track mouse position within the grid coordinate system:

function MouseTracking() {
  const [mousePos, setMousePos] = useState({ x: 0, y: 0 })

  const handleMouseMove = ({ x, y }: { x: number; y: number }) => {
    setMousePos({ x, y })
  }

  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <p style={{ position: 'absolute', top: 0, left: 0 }}>
        Mouse position: ({mousePos.x.toFixed(2)}, {mousePos.y.toFixed(2)})
      </p>
      <Grid config={{ onMouseMove: handleMouseMove }}>
        <GridItem x={mousePos.x} y={mousePos.y}>
          <div
            style={{
              width: 10,
              height: 10,
              background: 'red',
              borderRadius: '50%',
            }}
          />
        </GridItem>
      </Grid>
    </div>
  )
}

Untitled video - Made with Clipchamp

Navigation Controls

Keyboard Navigation

Navigate the grid using arrow keys:

function KeyboardNavigation() {
  return (
    <Grid
      config={{
        panStep: 50, // Pixels to move per arrow key press
        keyDisabled: false, // Enable keyboard controls
      }}
    >
      <GridItem x={0} y={0}>
        <div>Use arrow keys to pan around!</div>
      </GridItem>
    </Grid>
  )
}

Zoom Controls

Built-in zoom slider and wheel zoom functionality:

function ZoomDemo() {
  return (
    <Grid
      config={{
        minZoom: 0.25,
        maxZoom: 4,
        zoomSteps: 50,
        wheelDisabled: false, // Enable mouse wheel zoom
      }}
    >
      <GridItem x={0} y={0}>
        <div>Use mouse wheel or slider to zoom!</div>
      </GridItem>
    </Grid>
  )
}

Customization Options

Custom Grid Appearance

Customize the grid's visual appearance:

function CustomStyling() {
  return (
    <Grid
      config={{
        gridBackground: '#1b2845',
        gridColor: '#4f88cb', // Custom grid line color
        gridCellSize: 30, // Smaller grid cells
      }}
    >
      <GridItem x={0} y={0}>
        <div style={{ color: 'white' }}>Custom styled grid!</div>
      </GridItem>
    </Grid>
  )
}

Custom Zoom Slider

Replace the default zoom slider with your own component:

function CustomZoomSlider() {
  const customSlider = (sliderProps: SliderProps) => (
    <div
      style={{
        position: 'absolute',
        top: 20,
        right: 20,
        background: 'white',
        padding: '10px',
        borderRadius: '8px',
        boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
      }}
    >
      <label>Zoom: {sliderProps.zoomValue.toFixed(2)}x</label>
      <input
        type="range"
        min={Math.log10(sliderProps.minZoom)}
        max={Math.log10(sliderProps.maxZoom)}
        step={
          (Math.log10(sliderProps.maxZoom) - Math.log10(sliderProps.minZoom)) /
          (sliderProps.zoomSteps - 1)
        }
        value={Math.log10(sliderProps.zoomValue)}
        onChange={(e) => sliderProps.handleZoom(10 ** parseFloat(e.target.value))}
      />
    </div>
  )

  return (
    <Grid config={{ customZoomSlider: customSlider }}>
      <GridItem x={0} y={0}>
        <div>Custom zoom control!</div>
      </GridItem>
    </Grid>
  )
}

Disabling Features

Selective Feature Disabling

Disable specific interactions while keeping others active:

function SelectiveDisabling() {
  return (
    <div
      style={{
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        width: '100vw',
        height: '100vh',
        gap: '20px',
      }}
    >
      <div style={{ width: '30vw', aspectRatio: '1/1' }}>
        <Grid config={{ panDisabled: true, wheelDisabled: false }}>
          <GridItem x={0} y={0}>
            <div>Zoom only</div>
          </GridItem>
        </Grid>
      </div>
      {/* Wheel zoom disabled, pan still works */}
      <div style={{ width: '30vw', aspectRatio: '1/1' }}>
        <Grid config={{ panDisabled: false, wheelDisabled: true }}>
          <GridItem x={0} y={0}>
            <div>Pan only</div>
          </GridItem>
        </Grid>
      </div>
      {/* Everything disabled */}
      <div style={{ width: '30vw', aspectRatio: '1/1' }}>
        <Grid config={{ disabled: true }}>
          <GridItem x={0} y={0}>
            <div>Static view</div>
          </GridItem>
        </Grid>
      </div>
    </div>
  )
}

Grid Reference and Imperative Controls

Access grid methods using a ref:

import { useRef } from 'react'
import { Grid, GridRef } from 'dynamic-gridline'

function GridWithRef() {
  const gridRef = useRef<GridRef>(null)

  const focusGrid = () => {
    gridRef.current?.focusGrid()
  }

  return (
    <div>
      <button onClick={focusGrid}>Focus Grid (for keyboard controls)</button>
      <Grid ref={gridRef}>
        <GridItem x={0} y={0}>
          <div>Grid with ref access</div>
        </GridItem>
      </Grid>
    </div>
  )
}

Configuration Reference

The Grid component accepts a config prop with the following options:

| Property | Type | Default | Description | | ------------------ | ---------------------------------------------- | ------------------------------ | ---------------------------------------------- | | width | number | window.innerWidth * 10 | Total width of the grid canvas in pixels | | height | number | window.innerHeight * 10 | Total height of the grid canvas in pixels | | gridCellSize | number | 20 | Size of each grid cell in pixels | | gridBackground | string | 'transparent' | Background color of the grid container | | gridColor | string | 'oklch(70.7% 0.022 261.325)' | Color of the grid lines | | minZoom | number | 0.1 | Minimum zoom level (0.1 = 10%) | | maxZoom | number | 1 | Maximum zoom level (1 = 100%) | | zoomSteps | number | 100 | Number of steps in the zoom slider | | panStep | number | 10 | Pixels to move when using keyboard navigation | | disabled | boolean | false | Disable all interactions (pan, zoom, keyboard) | | panDisabled | boolean | false | Disable panning (mouse drag and touch) | | wheelDisabled | boolean | false | Disable mouse wheel zooming | | keyDisabled | boolean | false | Disable keyboard navigation | | customZoomSlider | ((props: SliderProps) => ReactNode) \| null | null | Custom zoom slider component | | onFastClick | ({ x, y }: { x: number; y: number }) => void | undefined | Callback for quick clicks on the grid | | onHoldClick | ({ x, y }: { x: number; y: number }) => void | undefined | Callback for long presses on the grid | | onMouseMove | ({ x, y }: { x: number; y: number }) => void | undefined | Callback for mouse movement over the grid |

GridItem Properties

| Property | Type | Default | Description | | -------------- | ----------- | ----------- | ----------------------------------- | | x | number | Required | X coordinate in grid space | | y | number | Required | Y coordinate in grid space | | disableScale | boolean | false | Prevent item from scaling with zoom | | fixedZIndex | number | undefined | Fixed z-index for layering control | | children | ReactNode | Required | Content to render in the grid item |

Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

Top contributors:

License

Distributed under the MIT License. See LICENSE for more information.

Contact

Omar Hassan - @omar_elfat76510 - [email protected]

Project Link: https://github.com/elfatairy/dynamic-gridline

Portfolio: https://omarhassan.net