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

@lieselthuriot/widget-landscape

v0.2.0

Published

Reusable Angular component for arranging widgets in a configurable dashboard grid.

Readme

Widget Landscape

@lieselthuriot/widget-landscape is a reusable Angular component for arranging @lieselthuriot/widget-renderer widgets in a configurable dashboard grid.

Installation

npm install @lieselthuriot/widget-landscape

@angular/common, @angular/core, and @lieselthuriot/widget-renderer are peer dependencies. The package supports Angular 21 and 22. npm, pnpm, and Bun install missing peer dependencies automatically; with Yarn, also add @lieselthuriot/widget-renderer.

Features

  • Fixed widget footprints based on widget size
  • Configurable column count with implicit rows
  • Landscape edit mode with palette, drag and drop, delete, and confirm/cancel
  • Delayed push-down collision previews while dragging
  • Transparent host surface with a dotted edit-mode pattern
  • Package-owned styling with no dependency on application design tokens
  • darkMode input forwarded to every WidgetRenderer and applied to package controls
  • Optional responsive reflow that preserves fixed widget dimensions
  • Persisted responsive preference with an edit-mode toggle for uncontrolled usage
  • Localized controls, with built-in German, French, and Dutch locales

Usage

Import WidgetLandscape and bind a LandscapeState. Supply palette entries with createWidget factories so every inserted item is independent. After confirmation, stateChange emits versioned landscape data containing the column count, responsive setting, and widget placements with only the widget ID, size, row, and column.

import { Component, signal } from '@angular/core';
import {
  LandscapePaletteItem,
  LandscapeState,
  LandscapeStateChange,
  WidgetLandscape,
} from '@lieselthuriot/widget-landscape';
import { WidgetInput } from '@lieselthuriot/widget-renderer';

const emptyWidget = (): WidgetInput => ({
  version: 2,
  id: crypto.randomUUID(),
  size: 'square',
  rows: [{ size: 1, unit: 'star' }],
  columns: [{ size: 1, unit: 'star' }],
  background: { color: '#172033', sensitive: false, repeat: false, size: 'cover', anchor: 'center' },
  refresh: { enabled: false, label: 'Refresh' },
  elements: [],
});

@Component({
  selector: 'app-dashboard',
  imports: [WidgetLandscape],
  template: `
    <widget-landscape
      [state]="state()"
      [palette]="palette"
      [darkMode]="false"
      (stateChange)="save($event)"
    />
  `,
})
export class Dashboard {
  readonly state = signal<LandscapeState>({
    version: 1,
    columns: 8,
    responsive: true,
    widgets: [{ id: 'welcome', size: 'square', row: 0, column: 0, widget: emptyWidget() }],
  });

  readonly palette: LandscapePaletteItem[] = [
    { id: 'empty', label: 'Empty widget', size: 'square', createWidget: emptyWidget },
  ];

  save(change: LandscapeStateChange): void {
    // Persist the layout; widget content is stored separately.
    console.log(change);
  }
}

The edit controls are shown when the palette has entries. Passing an empty array or null hides the edit controls, allowing consumers to provide a fixed layout without allowing widgets to be added, removed, or repositioned.

The component exposes state, palette, editMode, columnsLocked, darkMode, locale, rendererLocale, validationRules, and optional responsive inputs. rendererLocale (WidgetRendererLocaleOverrides) and validationRules (WidgetValidationRuleInput[]) are forwarded to every nested WidgetRenderer. Its outputs are stateChange, editModeChange, widgetSelected, widgetDeleted, refreshRequested, and actionRequested. Like the renderer, the landscape emits refresh and button events and leaves the requests to your application.

Localization

Visible landscape controls use English defaults and can be localized with the optional locale input. The input accepts partial overrides, so users can provide a complete locale or extend an existing locale with only the values they want to change. Widget content and palette labels remain consumer-provided values, so supply those in the language returned by your API or application translation layer.

import { WidgetLandscapeLocaleOverrides } from '@lieselthuriot/widget-landscape';

readonly landscapeLocale: WidgetLandscapeLocaleOverrides = {
  landscape: 'Widgetlandschap',
  columns: 'Kolommen',
  responsive: 'Responsief',
  cancel: 'Annuleren',
  confirm: 'Bevestigen',
  addWidget: 'Widget toevoegen',
  clearAll: 'Alles wissen',
  emptyEditMode: 'Kies een widget uit het palet om deze toe te voegen.',
  emptyLandscape: 'Open de bewerkingsmodus om een widget toe te voegen.',
  columnCount: (count) => `${count} ${count === 1 ? 'kolom' : 'kolommen'}`,
  refreshFallback: 'Widget vernieuwen',
  aria: {
    columns: 'Kolommen van het landschap',
    responsive: 'Responsief widgetlandschap',
    edit: 'Widgetlandschap bewerken',
    widgets: 'Widgets',
    widgetFootprint: (columns, rows) => `Widgetafmetingen: ${columns} × ${rows}`,
    selectWidget: 'Widget selecteren',
    deleteWidget: 'Widget verwijderen',
  },
};

For a small customization, override only the values you need:

import {
  frenchWidgetLandscapeLocale,
  WidgetLandscapeLocaleOverrides,
} from '@lieselthuriot/widget-landscape';

readonly landscapeLocale: WidgetLandscapeLocaleOverrides = {
  ...frenchWidgetLandscapeLocale,
  clearAll: 'Supprimer les widgets',
};
<widget-landscape [state]="state" [palette]="palette" [locale]="landscapeLocale" />

The aria property contains the labels owned by the landscape, including the column control, responsive toggle, edit action, palette, widget selection, deletion, and palette footprint. It supports partial nested overrides:

const customLocale: WidgetLandscapeLocaleOverrides = {
  aria: {
    deleteWidget: 'Supprimer le widget',
  },
};

ARIA labels and other text from nested WidgetRenderer instances are localized separately through the landscape's rendererLocale input, which accepts the renderer's locale overrides (for example frenchWidgetRendererLocale from @lieselthuriot/widget-renderer). When the application language changes, replace the locale and refetch the localized widget contracts. State updates received while editing continue to follow the component's existing draft behavior.

While editing, users can change the column count unless columnsLocked is true. Column changes are draft-only until confirmation; cancelling edit mode restores the original count and layout. The confirmed count is emitted as stateChange.columns.

The surface is transparent. The package does not inspect html[data-theme] and does not require application-level design tokens. Consumers can override package variables such as --widget-landscape-accent from their host stylesheet.

responsive defaults to false. When enabled, the landscape keeps its existing fixed column and row dimensions and measures the available host width. Widgets that do not fit in the available columns are visually reflowed into additional rows instead of being resized. Responsive reflow is presentation-only: the configured columns and widget placements are unchanged until an edit is explicitly confirmed.

If responsive is omitted, the value is read from LandscapeState and a Responsive toggle is available in edit mode. Toggling it changes the draft only. Confirming emits and persists the selected value; cancelling restores the previous value. Binding [responsive] to true or false makes the component controlled and hides the toggle.

State and footprints

LandscapeState and LandscapeStateChange use version: 1. A state widget contains its WidgetInput while a state change deliberately contains only placement data, so applications can persist layout separately from widget content. Use normalizeLandscapeState when accepting state from an external source.

| Widget size | Columns | Rows | | --- | ---: | ---: | | mini | 1 | 1 | | compact | 2 | 1 | | tall | 1 | 2 | | square | 2 | 2 | | wide | 4 | 2 | | mega | 4 | 4 | | banner | 2 | 4 | | hero | 8 | 4 |

Unknown widget sizes use the square footprint.

Public API

Import everything from @lieselthuriot/widget-landscape. The entry point exports WidgetLandscape, the state, palette, and event types (LandscapeState, LandscapeStateChange, LandscapeWidget, LandscapeWidgetPlacement, LandscapePaletteItem, LandscapeActionEvent, LandscapeRefreshEvent), normalizeLandscapeState, footprint and layout helpers such as landscapeFootprints and landscapeFootprintForSize, and the locales defaultWidgetLandscapeLocale, germanWidgetLandscapeLocale, frenchWidgetLandscapeLocale, and dutchWidgetLandscapeLocale. The complete list is maintained in src/public-api.ts.

Contributing

The package is developed in the widget-studio repository, together with the editor, the renderer, and the C# contracts. See the repository README for build, test, and release instructions. Report problems in the issue tracker.

License

MIT