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

@wincc-oa/wui-ui-elements

v2.0.1

Published

WinCC Open Architecture Dashboard project.

Readme

WinCCOA WebComponent Dashboard

This package is part of the workspace for the WinCC Open Architecture WebComponent Dashboard, built using Lit and managed with Nx.

Usage information and reference details can be found in the WinCC OA documentation.

Components in this package

This package holds presentational WebComponents. Some examples:

  • wui-grid-layout, wui-list-layout, wui-schematic-layout, wui-map-layout - layout components. Each arranges a list of items and lets the user move, resize, or add to them.
  • wui-layout - picks one of the layout components above based on a render type.
  • wui-layout-host - hosts a layout and wires it to the edit session (dirty tracking, inner layout reference).
  • wui-search-bar, wui-nav-tree, wui-tree-table, wui-datetime-range-picker, wui-datetime-range-button - other standalone UI components.

Using a layout

A layout takes a value (a list of items, each with an id and optional position/size fields such as x, y, cols, rows) and a settings object for that layout type. An item may carry any extra data your own code needs - the layout does not look inside it.

To control how each item is drawn, pass a strategy:

import '@wincc-oa/wui-ui-elements/wui-grid-layout/wui-grid-layout.js';
import { LayoutItemStrategyBuilder } from '@wincc-oa/wui-ui-elements/model/layout-item-strategy.js';
import { html } from 'lit';

const strategy = new LayoutItemStrategyBuilder().withRender(({ item }) => html`<div class="tile">${item.data.label}</div>`).build();

const value = [{ id: 'a', x: 0, y: 0, cols: 2, rows: 2, data: { label: 'Hello' } }];
<wui-grid-layout .value="${value}" .settings="${{}}" .strategy="${strategy}"></wui-grid-layout>

A layout works without a strategy too - each item is then shown as plain text, which is enough to try a layout out before writing a real renderer for it.

Besides drawing an item, a strategy can also read a dropped item to suggest a size for it, and turn a pasted image into item data. Both are optional and only apply if you need them:

const strategy = new LayoutItemStrategyBuilder()
  .withRender(({ item }) => html`<div class="tile">${item.data.label}</div>`)
  .withDragInterpreter((raw) => ({ size: { cols: raw.cols, rows: raw.rows } }))
  .withImagePaste((image) => ({ label: image.src }))
  .build();

wui-layout: picking a layout by render type

Most consumers do not pick a concrete layout tag directly. wui-layout renders the matching layout (grid, schematic, list, or map) based on settings.renderType, and forwards value, disabled, strategy, and the focus-x/focus-y/focus-z properties to it.

<wui-layout .settings="${settings}" .value="${value}" .strategy="${strategy}"></wui-layout>

| Property | Type | Description | | -------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | | settings | WuiLayoutSettings | Layout config; settings.renderType wins over the renderType property | | value | LayoutItem<D>[] | Item configurations | | renderType | 'grid' \| 'schematic' \| 'list' \| 'map' | Fallback layout type when settings has none set (default grid) | | disabled | boolean | Disables editing operations on the inner layout | | strategy | LayoutItemStrategy<unknown> | Forwarded to the inner layout unchanged | | focusX / focus-x | number \| undefined | Forwarded to every layout; meaning is layout-specific (e.g. map's latitude). Only a layout with a notion of "current view" acts on it | | focusY / focus-y | number \| undefined | See focusX | | focusZ / focus-z | number \| undefined | See focusX | | focusId / focus-id | string \| undefined | ID of the widget to select on mount, e.g. the widget just saved | | fitContainer / fit-container | boolean | Grid only: clamp to a fixed-size parent and scroll internally (nested grids). Off = grow with rows (top-level). See "Sizing & Scaling". |

Listen for wui:layoutready (detail { layout }) to reach the inner layout instance for imperative calls (paste, elementUpdater, etc.).

wui-layout-host: wiring a layout into the edit session

wui-layout-host extends wui-layout with edit-session wiring: it calls session.markDirty() on the consumed editSessionContext whenever the inner layout dispatches change. disabled stays a plain inherited property - the caller decides it (e.g. from canEdit, a dashboard-permission concept wui-layout-host does not know about). The wui:requestadd event from the inner layout needs no re-dispatch: it already bubbles and crosses shadow-DOM boundaries on its own (bubbles: true, composed: true).

<wui-layout-host .value="${items}" .settings="${settings}" .strategy="${strategy}"></wui-layout-host>

| Property/Event | Description | | ---------------- | ---------------------------------------------------------------------------------- | | layout | Getter returning the currently-mounted concrete layout instance | | wui:requestadd | Bubbles up from the inner layout unchanged, no listener or re-dispatch needed here |

wui-layout-host has no dashboard-specific properties. It has no WuiDashboardLayoutHost subclass either. The WinCC OA dashboard binds its widget list directly to .value.

Sizing & Scaling

How a layout fills its parent is driven by settings, not CSS:

See docs/knowledge/project/webui-runtime-layouts.md for the per-layout sizing/scaling internals table.

Give the layout (or wui-layout/wui-layout-host) a parent with an explicit height. Outside the dashboard shell these components have no implicit size.

Base Class API Reference

WuiBaseLayout<T extends LayoutItem<D, G>, D, TSettings extends LayoutSettingsBase, G>

G is the layout-specific geometry type (for example LayoutItemGridGeometry for grid, LayoutItemSchematicGeometry for schematic). Each concrete layout intersects its own G onto LayoutItem so existing field access (item.x, item.cols, and so on) keeps working per layout.

Abstract base class for all concrete layout implementations (WuiGridLayout, WuiListLayout, WuiSchematicLayout, WuiMapLayout).

Properties

| Property | Type | Description | | ------------------------------------ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------- | | settings | TSettings | Layout configuration | | value | T[] \| undefined | Array of layout items | | defaultValue | T[] \| undefined | Persisted baseline; dirty compares value against this | | dirty | boolean | Read-only, computed by structural comparison of value vs defaultValue | | disabled | boolean | Disables all editing operations | | readonly | boolean | Reflected attribute; layout-specific meaning | | loading | boolean | Reflected attribute | | focusX/focusY/focusZ/focusId | number \| string \| undefined | Geometry of the widget just saved, forwarded from the add-widget page; field meaning is layout-specific | | strategy | LayoutItemStrategy<unknown> \| undefined | Delegates item rendering/drag/paste; see "Using a layout" above |

dirty has no public setter and never dispatches an event on its own - it is purely the result of comparing value against defaultValue by item id and geometry fields, recomputed on every read.

Abstract Methods (Must Override)

| Method | Signature | Description | | ------------------------ | ----------------------------------------------------------- | ------------------------------------------------------------------------------- | | retrieveValue | (event) => T[] | Returns items for clipboard copy/cut | | getId | (element: T) => string \| number | Returns unique ID of an item | | getCapabilities | () => LayoutCapabilities | Describes which features this layout supports | | getLayoutItems | () => Element[] (protected) | Returns all rendered item elements in the layout | | getItemIdAttribute | () => string (protected) | Returns the attribute name that holds the item ID | | getItemContentSelector | () => string (protected) | Returns CSS selector for the content element inside an item | | pasteWidgetJson | (widget: T) => void (protected) | Inserts a pasted item into the layout | | buildPastedImageItem | (image: HTMLImageElement, data: unknown) => T (protected) | Combines layout-owned geometry with strategy-supplied data for a pasted image |

LayoutCapabilities describes which interactive features are available at runtime:

interface LayoutCapabilities {
  supportsDragDrop: boolean; // widgets can be dragged within the layout
  supportsResize: boolean; // widgets have resize handles
  supportsRotation: boolean; // widgets can be rotated (typically only schematic)
  supportsZIndex: boolean; // widgets support z-index layering
  supportsMobileMode: boolean; // layout adapts to mobile screen sizes
  supportsAutoPosition: boolean; // new widgets are auto-positioned
  supportsOutOfBoundsFix: boolean; // layout implements fixOutOfBounds() to repair items outside the visible area
}

Virtual Methods (Can Override)

| Method | Default Behavior | Override When | | ------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | elementUpdater | Returns list unchanged | Layout needs drag-drop from a selector | | handleCopy | Writes to clipboard via ClipboardController | Custom copy behavior needed | | handleCut | Copies then deletes items | Custom cut behavior needed | | handlePaste | Reads from clipboard controller | Custom paste/positioning needed | | handleDelete | Removes from value array | Layout-specific cleanup needed | | handleUpdate | Updates item in value array | Layout-specific update logic | | fixOutOfBounds | No-op | Layout can end up in an invalid state and needs repair (see LayoutCapabilities.supportsOutOfBoundsFix) | | toLocalSize | Pass-through (grid-cell units unchanged) | Layout's on-screen unit differs from grid cells | | onEditModeChanged | No-op | Layout needs to react to disabled changes |

Protected Utilities (Use Directly)

| Member | Description | | -------------------- | ------------------------------------------------------------ | | indexById(item: T) | Find array index by item ID | | isItemSaved(id) | Whether an item with this id is present in defaultValue | | clipboard | ClipboardController instance for copy/paste | | reset() | Restores value from defaultValue and dispatches change |

Selection

| Member | Description | | -------------- | ----------------------------------------------------------------------------------- | | selectedItem | Getter: T \| undefined. Reads whichever item currently has the marked attribute | | select event | bubbles: true, composed: true. Fires on every mark and unmark transition |

A consumer can listen for select on wui-layout or wui-layout-host directly. The event crosses shadow-DOM boundaries on its own, the same composed: true mechanism that lets editSessionContext reach descendants without an intermediate re-provide.

Layout Comparison

| Feature | Grid Layout | Schematic Layout | List Layout | Map Layout | | ------------------- | ------------------------- | ------------------ | ----------------------- | ----------------------------------------------------- | | Positioning | Cell-based (columns/rows) | Pixel-based (x/y) | Order-based (flex) | Geographic (latitude/longitude) | | Auto-position | Yes (finds empty space) | No (manual only) | Yes (reflow on resize) | No (manual only) | | Collision detection | Yes | No | No | No | | Rotation | No | Yes | No | No | | Z-index | No | Yes | No | No (zoom means base zoom level, see wui-map-layout) | | Mobile responsive | Yes (single column) | No | Yes (wraps) | No | | Background image | Color only | Color + Image | Color only | No (tile layer instead) | | Resize handles | Built-in (GridStack) | Custom SVG handles | None (fixed-size items) | Custom corner handle |

wui-list-layout

A flex-based layout that arranges same-sized widgets. Items reflow automatically when the container is resized - no JS needed, pure CSS flex-wrap.

Scale Modes

Controlled via ListLayoutConfig.elementScaling:

| Mode | Behavior | | --------------- | ------------------------------------------------------ | | fixed-element | Items have fixed pixel size, wrap as container resizes | | fixed-height | Single horizontal row, container height = item height | | fixed-width | Single vertical column, container width = item width |

For fixed-element, set flowDirection: 'row' (horizontal wrap, default) or 'column' (vertical wrap).

Example

import '@wincc-oa/wui-ui-elements/wui-list-layout/wui-list-layout.js';
import type { ListLayoutConfig } from '@wincc-oa/wui-ui-elements/model/layout-config.js';

const settings: ListLayoutConfig = {
  elementScaling: 'fixed-element',
  flowDirection: 'row',
  gap: 4,
  elementWidth: 160,
  elementHeight: 120
};

const value = [{ id: 'gauge-1', data: {/* your own render data */} }];

// In a Lit template:
html`<wui-list-layout .settings=${settings} .value=${value} .strategy=${strategy}></wui-list-layout>`;

Removing the default border

wui-list-layout {
  --wui-layout-border: none;
}

wui-map-layout

A Leaflet-based layout that places widgets on a geographic map by latitude/longitude, with markers that collapse to a marker or cluster when zoomed far out.

Settings

| Setting | Type | Editable in form? | Description | | ----------------- | --------- | ------------------ | --------------------------------------------------------------------------- | | lat | number | Yes | Starting latitude. Falls back to 51 if unset. | | lng | number | Yes | Starting longitude. Falls back to 10 if unset. | | zoom | number | Yes | Starting zoom level. Falls back to 6 if unset. | | tileUrl | string | Yes | Tile layer URL template. Falls back to the OpenStreetMap tile URL if unset. | | tileAttribution | string | Yes | Tile layer attribution text. Falls back to the OpenStreetMap attribution. | | renderOffscreen | boolean | No (settings-only) | Disables viewport culling (see below). Defaults to false. |

See docs/knowledge/project/webui-runtime-layouts.md for the tile-fallback-ownership and settings-only-escape-hatch internals behind renderOffscreen and tileUrl.

Background color and image are hidden from the edit form for renderType: 'map', since the tile layer covers the whole viewport and this layout never applies them.

Required WinCC OA project setting

Map tiles are loaded from a host outside the WinCC OA web server, so the project has to allow external resources. Without this the tiles are blocked and the map stays empty. Add to the project's config file:

[wssServer]
allowExternalResources = 1

Choosing a tile server

Leaving tileUrl empty uses the OpenStreetMap community tile servers - fine for demos and local development. For production, point tileUrl at a tile server the installation controls and set tileAttribution to whatever that source's license requires. Both are editable in the dashboard edit form, each with a tooltip linking to the Leaflet TileLayer documentation for the {z}/{x}/{y} URL format. See docs/knowledge/project/webui-runtime-layouts.md for why a production deployment should not rely on the OpenStreetMap default.

Example

import '@wincc-oa/wui-ui-elements/wui-map-layout/wui-map-layout.js';
import type { MapLayoutConfig } from '@wincc-oa/wui-ui-elements/model/layout-config.js';

const settings: MapLayoutConfig = {
  lat: 48.2082,
  lng: 16.3738,
  zoom: 12
  // tileUrl/tileAttribution omitted: falls back to OpenStreetMap.
};
<wui-map-layout .settings="${settings}" .value="${value}"></wui-map-layout>

Geometry fields

wui-map-layout uses LayoutItemMapGeometry, its own geometry shape for geographic placement:

| Field | Meaning on the map | | ---------------- | ------------------------------------------------------------------------------------------ | | lat | Latitude of the widget's geographic center (the marker is centered, not anchored top-left) | | lng | Longitude of the widget's geographic center | | zoom | Base zoom level the widget targets, driving render-mode switching (see below) | | width/height | Pixel width/height of the widget at its base zoom, centered on lat/lng |

See docs/knowledge/project/webui-runtime-layouts.md for the render-mode-switching logic and viewport-culling internals.

License

MIT