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

keystone-dashboard-layout-angular

v1.0.0

Published

Angular port of the responsive & dynamic grid / dashboard layout with drag, drop and resizable actions.

Readme

CI code style: prettier npm NPM

What this actually is

An Angular, TypeScript-native library for building draggable, resizable, responsive dashboard layouts — the kind of thing you'd use to let a user rearrange widgets, charts, or panels on a screen, with drag, resize, responsive breakpoints, multi-select, undo/redo, and collision handling built in.

It is not a data table/grid. If you're after sorting, filtering, paging, or spreadsheet-style rows and columns, this isn't that. Built as standalone components (no NgModule required) on the same native, Pointer-Events-based drag/resize engine shared with the Vue and React packages in this family, rather than a separate implementation of the hard, easy-to-get-subtly-wrong parts (collision, compaction, responsive breakpoint math).

Quick start

npm install keystone-dashboard-layout-angular
import { Component } from '@angular/core';
import { GridLayoutComponent, GridItemComponent } from 'keystone-dashboard-layout-angular';
import type { TLayout } from 'keystone-dashboard-layout-core';

@Component({
  selector: 'app-dashboard',
  standalone: true,
  imports: [GridLayoutComponent, GridItemComponent],
  template: `
    <kdl-grid-layout [layout]="layout" (layoutChange)="layout = $event">
      @for (item of layout; track item.i) {
        <kdl-grid-item [i]="item.i" [x]="item.x" [y]="item.y" [w]="item.w" [h]="item.h">
          Item {{ item.i }}
        </kdl-grid-item>
      }
    </kdl-grid-layout>
  `,
})
export class DashboardComponent {
  layout: TLayout = [
    { i: 'a', x: 0, y: 0, w: 2, h: 2 },
    { i: 'b', x: 2, y: 0, w: 2, h: 2 },
  ];
}

Don't forget the stylesheet, imported once wherever it'll load application-wide:

import 'keystone-dashboard-layout-angular/style.css';

@angular/common/@angular/core (^17.0.0 || ^18.0.0 || ^19.0.0) and rxjs (^7.8.0) are peer dependencies.

GridLayoutComponent is a fully controlled component — it never mutates the layout array (or any item in it) you pass in; every drag/resize tick (and the compaction that follows it) is reported via a layoutChange @Output() with a brand-new array. GridItemComponent takes i/x/y/w/h as its own required @Input()s — bind each one explicitly per item in your own template's @for loop.

Using just the grid math, without Angular? keystone-dashboard-layout-core exports the same collision detection, compaction, movement, and alignment functions this package is built on — zero Angular dependency, no live DOM required, plain data in and out:

import { collides, compactLayout, moveElement } from 'keystone-dashboard-layout-core';

Features

  • Core layout — grid-unit positioning with automatic pixel conversion, a fully controlled layout/layoutChange contract, auto-sizing container (autoSize/heightMode, including 'scroll'/'fit' modes) and per-item autoHeight, visible grid lines, visual alignment guides and magnetic snapToGrid (a real distinction — one shows where edges line up, the other actually moves the item), CSS transform positioning.
  • A grid-wide behavioral cascade — isDraggable/isResizable/ isBounded/isMirrored/maxRows/showCloseButton/enableEditMode/ useBorderRadius/borderRadiusPx/ariaLabels can each be set once on GridLayoutComponent and inherited by every GridItemComponent, or overridden per-item when needed.
  • Drag and resize — drag from anywhere on an item by default, or restrict it to a handle (dragAllowFrom/dragIgnoreFrom); resize from all eight edges/corners with cursor affordance and optional visible handles (showResizeHandles/resizeHandleColor); bounded dragging (isBounded); aspect-ratio locking (preserveAspectRatio); per-item size constraints (minW/maxW/minH/maxH); keyboard move/resize (arrow keys / Shift+arrow, RTL-aware); a moveBlockedByCollision @Output() for shake/flash/toast feedback.
  • Collision and compaction — vertical/horizontal/none compaction plus overlap variants (compactType), preventCollision, horizontalShift, static items excluded from cascades, on-demand compactNow()/rearrange(), collision-safe duplicateItem(id), a pluggable compactor input for replacing the algorithm entirely.
  • Responsive layouts — breakpoint-driven column counts (responsive/breakpoints/cols), predefined layouts per breakpoint (responsiveLayouts) with auto-generation for any breakpoint without one, distributeEvenly for spreading out-of-bounds items instead of clamping them.
  • Multi-select and group operations — click/Shift-click/Ctrl-click selection, group move/resize, align/distribute commands (alignSelected/distributeSelected).
  • Multi-grid and drag-and-drop — drag items between independent GridLayoutComponent instances (allowCrossGridDrag) via a real first-fit bin-pack, and from outside the grid system entirely via native HTML5 drag-and-drop (allowOutsideDrop, outsideDropAccept to reject incompatible drags).
  • Editing and lifecycle — a built-in close button (showCloseButton
    • a removeItem @Output()), an edit-mode toggle (enableEditMode), add/remove items without manual position math, opt-in undo/redo (enableUndoRedo/undoHistoryLimit) at committed-change granularity — including externally-driven layout changes, not just drag/resize.
  • Styling and customization — configurable border radius, transition duration/easing, a [kdlGridItemHeader] marker directive for a dedicated header region, standalone GridItemCloseButtonComponent/ GridItemDragHandleComponent utility components, automatic RTL mirroring (isMirrored, grid-wide or per-item).
  • Persistence — GridLayoutStorageService for a single saved layout, plus GridLayoutPresetsService for saving and switching between several named arrangements — both providedIn: 'root', taking/returning a plain layout value directly.
  • Export — exportLayoutAsSvg(), a dependency-free grid-to-image export for a report, thumbnail, or "share my dashboard" feature.
  • Accessibility — keyboard move/resize, aria-roledescription/ role="group" on interactive items, localizable UI/ARIA strings (ariaLabels, grid-wide default + per-item override).

Imperative API

Reach GridLayoutComponent's own public methods via a template reference variable:

import { Component, ViewChild } from '@angular/core';
import { GridLayoutComponent, GridItemComponent } from 'keystone-dashboard-layout-angular';

@Component({
  standalone: true,
  imports: [GridLayoutComponent, GridItemComponent],
  template: `
    <button (click)="grid.compactNow()">Tidy up</button>
    <kdl-grid-layout #grid [layout]="layout" (layoutChange)="layout = $event">
      <!-- ... -->
    </kdl-grid-layout>
  `,
})
export class DashboardComponent {
  @ViewChild('grid') gridRef!: GridLayoutComponent;
}

compactNow()/rearrange(), duplicateItem(id), undo()/redo()/ canUndo/canRedo, selectItem()/deselectItem()/ toggleItemSelection()/clearSelection()/selectedItemIds, alignSelected(edge)/distributeSelected(axis), exportLayoutAsSvg(options?), scrollToItem(id)/focusItem(id).

What this package exports

import {
  GridLayoutComponent,
  GridItemComponent,
  GridItemHeaderDirective,
  GridLayoutStorageService,
  GridLayoutPresetsService,
  GridItemDragHandleComponent,
  GridItemCloseButtonComponent,
} from 'keystone-dashboard-layout-angular';

Layout-level types (TLayout, ILayoutItem, ECompactType, and so on) come from keystone-dashboard-layout-core instead — an Angular @Component's own class already is its prop contract, so there's no separate props-interface convention to import here the way React's IGridLayoutProps/IGridItemProps work.

Idiomatic Angular, not a transliteration

Standalone components throughout (no NgModule required), a DI-scoped GridEventBusService instead of Vue's provide/inject or React's Context, providedIn: 'root' services instead of ref-bound composables/ hooks, and a marker directive (GridItemHeaderDirective) queried via @ContentChild instead of a named slot or render prop — the same underlying behavior, expressed the way Angular actually works.

Shared engine, three frameworks

This package, keystone-dashboard-layout-vue, and keystone-dashboard-layout-react all build on keystone-dashboard-layout-core for collision detection, compaction, responsive breakpoint math, and the native Pointer-Events-based drag/resize engine — one implementation of the hard parts, not three independently-maintained copies that could drift out of sync.

Testing

A real, extensive unit/component test suite (Jest + jest-preset-angular) backs every feature, with Stryker mutation testing configured. A real end-to-end browser test layer (Playwright) runs against a dedicated e2e-fixture/ application — matching the Vue/React packages' own e2e setup, not a Vite dev server the way those two use, since this package's Angular target hit a genuinely unresolved upstream @analogjs/vite-plugin-angular bug. Karma (still present in this repo) predates the Playwright suite and is no longer needed for it — kept for now rather than removed in the same pass that added real e2e coverage.

Changelog

See CHANGELOG.md for release history.

Donate

If you enjoyed this project — or just feeling generous, consider buying me a 🍺. Cheers!

License

MIT