JSPM

dynamic-gridline

1.0.5
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 6
  • Score
    100M100P100Q73209F
  • License ISC

A draggable and zoomable grid system for placing items.

Package Exports

    This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (dynamic-gridline) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

    Readme

    Contributors Forks Stargazers Issues project_license LinkedIn


    Logo

    Dynamic Gridline

    A draggable and zoomable grid system for placing items.

    Report Bug · Request Feature

    Table of Contents
    1. About The Project
    2. Getting Started
    3. Usage
    4. Roadmap
    5. Contributing
    6. License
    7. Contact
    8. Acknowledgments

    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>
      )
    }
    Screenshot 2025-09-21 011243

    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>
      )
    }
    Screenshot 2025-09-21 011342

    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>
      )
    }
    Screenshot 2025-09-21 011534

    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>
      )
    }
    Screenshot 2025-09-21 011732 Screenshot 2025-09-21 013624

    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>
      )
    }
    Screenshot 2025-09-21 011845

    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

    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>
      )
    }
    Screenshot 2025-09-21 012631

    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>
      )
    }
    Screenshot 2025-09-21 012823

    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>
      )
    }
    Screenshot 2025-09-21 013157

    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:

    contrib.rocks image

    License

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

    Contact

    Omar Hassan - @omar_elfat76510 - elfatairy@omarhassan.net

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

    Portfolio: https://omarhassan.net

    (back to top)