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

@pebbledash/web-component

v1.0.2

Published

<ud-dashboard> custom element wrapping the DOM renderer

Readme

@pebbledash/web-component

Full-featured custom element <ud-dashboard> for framework-agnostic usage with declarative attributes and custom events.

Installation

pnpm add @pebbledash/web-component @pebbledash/renderer-dom @pebbledash/core

Quick Start

<!DOCTYPE html>
<html>
<head>
  <style>
    ud-dashboard {
      display: block;
      width: 100%;
      height: 500px;
      border: 1px solid #ccc;
    }
  </style>
</head>
<body>
  <ud-dashboard
    id="dashboard"
    mode="resize"
    min-tile-width="10"
    min-tile-height="10"
    overlays
    keyboard
  ></ud-dashboard>

  <script type="module">
    import '@pebbledash/web-component';

    const dashboard = document.getElementById('dashboard');
    
    // Set widgets
    dashboard.widgets = {
      default: (ctx) => ({
        mount: () => { ctx.element.textContent = `Tile ${ctx.tileId}`; },
        unmount: () => { ctx.element.textContent = ''; },
      }),
    };
    
    // Set initial layout
    dashboard.initialLayout = {
      tiles: [
        { id: 'tile-1', x: 0, y: 0, width: 50, height: 100 },
        { id: 'tile-2', x: 50, y: 0, width: 50, height: 100 },
      ],
    };
    
    // Listen for events
    dashboard.addEventListener('ud-tile-click', (e) => {
      console.log('Clicked tile:', e.detail.tileId);
    });
    
    dashboard.addEventListener('ud-ready', (e) => {
      console.log('Dashboard ready!');
    });
  </script>
</body>
</html>

Attributes

Configure the dashboard declaratively using HTML attributes:

| Attribute | Type | Description | |-----------|------|-------------| | mode | 'insert' \| 'resize' | Current interaction mode | | min-tile-width | number | Minimum tile width (%) | | min-tile-height | number | Minimum tile height (%) | | max-tiles | number | Maximum number of tiles | | overlays | boolean | Enable edge overlays (presence = true) | | keyboard | boolean | Enable keyboard navigation | | keyboard-undo-redo | boolean | Enable Ctrl+Z / Ctrl+Shift+Z | | keyboard-delete | boolean | Enable Delete key for tiles | | use-shadow-dom | boolean | Use Shadow DOM for encapsulation |

<ud-dashboard
  mode="insert"
  min-tile-width="15"
  min-tile-height="15"
  max-tiles="20"
  overlays
  keyboard
  keyboard-undo-redo
></ud-dashboard>

Properties

Set complex values via JavaScript properties:

| Property | Type | Description | |----------|------|-------------| | model | DashboardModel | Underlying model (read-only after mount) | | widgets | WidgetRegistry | Widget factories by type | | initialLayout | InitialLayout | Initial tile positions | | config | UDDashboardConfig | Full configuration object | | mode | 'insert' \| 'resize' | Current mode (read/write) |

const dashboard = document.querySelector('ud-dashboard');

// Set widgets
dashboard.widgets = {
  default: createDefaultWidget,
  chart: createChartWidget,
};

// Set initial layout
dashboard.initialLayout = {
  tiles: [
    { id: 'tile-1', x: 0, y: 0, width: 100, height: 100, meta: { widgetType: 'chart' } },
  ],
};

// Set full config
dashboard.config = {
  minTile: { width: 10, height: 10 },
  maxTiles: 16,
  overlays: true,
  keyboard: true,
  keyboardUndoRedo: true,
  resizeConfig: {
    redistributeEqually: true,
  },
};

// Change mode
dashboard.mode = 'resize';

Events

Listen for dashboard events using standard addEventListener:

| Event | Detail | Description | |-------|--------|-------------| | ud-tile-click | { tileId, originalEvent } | Tile was clicked | | ud-tile-dblclick | { tileId, originalEvent } | Tile was double-clicked | | ud-tile-hover | { tileId, entering, originalEvent } | Pointer entered/left tile | | ud-tile-focus | { tileId, focused } | Tile received/lost focus | | ud-tile-contextmenu | { tileId, originalEvent } | Right-click on tile | | ud-history-change | { canUndo, canRedo } | Undo/redo state changed | | ud-mode-change | { mode, previousMode } | Mode switched | | ud-container-resize | { width, height } | Container was resized | | ud-resize-start | { tileId, edge } | Resize drag started | | ud-resize-move | { tileId, edge, delta, clamped } | Resize dragging | | ud-resize-end | { tileId, edge, committed } | Resize drag ended | | ud-tiles-change | { tiles } | Tiles state changed | | ud-ready | { dashboard } | Dashboard mounted and ready |

const dashboard = document.querySelector('ud-dashboard');

dashboard.addEventListener('ud-tile-click', (e) => {
  const { tileId } = e.detail;
  console.log('Clicked:', tileId);
});

dashboard.addEventListener('ud-history-change', (e) => {
  const { canUndo, canRedo } = e.detail;
  undoBtn.disabled = !canUndo;
  redoBtn.disabled = !canRedo;
});

dashboard.addEventListener('ud-mode-change', (e) => {
  const { mode } = e.detail;
  insertBtn.classList.toggle('active', mode === 'insert');
  resizeBtn.classList.toggle('active', mode === 'resize');
});

dashboard.addEventListener('ud-tiles-change', (e) => {
  const { tiles } = e.detail;
  console.log('Tiles:', tiles.length);
});

Methods

Call methods on the element for programmatic control:

| Method | Arguments | Returns | Description | |--------|-----------|---------|-------------| | undo() | - | void | Undo last operation | | redo() | - | void | Redo last undone operation | | canUndo() | - | boolean | Check if undo available | | canRedo() | - | boolean | Check if redo available | | announce(message) | string | void | Screen reader announcement | | getTiles() | - | Tile[] | Get all tiles | | splitTile(id, opts) | TileId, { orientation, ratio? } | Promise<TileId> | Split a tile | | deleteTile(id) | TileId | Promise<boolean> | Delete a tile | | startConfigPreview(config) | PartialExtendedConfig | void | Start config preview | | endConfigPreview() | - | void | End config preview | | isConfigPreviewActive() | - | boolean | Check preview active | | getPreviewAffectedTiles() | - | TileId[] | Get affected tiles |

const dashboard = document.querySelector('ud-dashboard');

// Toolbar buttons
undoBtn.onclick = () => dashboard.undo();
redoBtn.onclick = () => dashboard.redo();

// Get tiles
const tiles = dashboard.getTiles();
console.log('Tiles:', tiles);

// Split a tile
const newTileId = await dashboard.splitTile('tile-1', {
  orientation: 'vertical',
  ratio: 0.5,
});

// Delete a tile
const success = await dashboard.deleteTile('tile-2');

Shadow DOM

Enable Shadow DOM for style encapsulation:

<ud-dashboard use-shadow-dom></ud-dashboard>

Events bubble through the Shadow DOM boundary (composed: true).

Styling

Style the dashboard using CSS custom properties:

ud-dashboard {
  /* Edge overlays */
  --ud-edge-color: rgba(0, 0, 0, 0.1);
  --ud-edge-hover-color: #3b82f6;
  --ud-edge-active-color: #2563eb;
  
  /* Boundary indicators */
  --ud-boundary-color: rgba(59, 130, 246, 0.3);
  --ud-boundary-active-color: rgba(59, 130, 246, 0.6);
}

ud-dashboard .ud-tile {
  background: white;
  border-radius: 4px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

ud-dashboard .ud-tile:focus {
  outline: 2px solid #3b82f6;
}

TypeScript Support

Full TypeScript support with exported types:

import '@pebbledash/web-component';
import type {
  UDDashboard,
  UDDashboardConfig,
  UDDashboardEventMap,
  TileEventDetail,
  TileHoverEventDetail,
  HistoryChangeEventDetail,
  ModeChangeEventDetail,
  TilesChangeEventDetail,
} from '@pebbledash/web-component';

const dashboard = document.querySelector('ud-dashboard') as UDDashboard;

// Type-safe event handling
dashboard.addEventListener('ud-tile-click', (e: CustomEvent<TileEventDetail>) => {
  console.log('Tile:', e.detail.tileId);
});

dashboard.addEventListener('ud-history-change', (e: CustomEvent<HistoryChangeEventDetail>) => {
  console.log('Can undo:', e.detail.canUndo);
});

Framework Integration

Vue

<template>
  <ud-dashboard
    ref="dashboard"
    mode="resize"
    overlays
    keyboard
    @ud-tile-click="handleClick"
    @ud-ready="handleReady"
  />
</template>

<script setup>
import '@pebbledash/web-component';
import { ref, onMounted } from 'vue';

const dashboard = ref(null);

onMounted(() => {
  dashboard.value.widgets = { default: createWidget };
  dashboard.value.initialLayout = { tiles: [...] };
});

function handleClick(e) {
  console.log('Clicked:', e.detail.tileId);
}
</script>

Svelte

<script>
  import '@pebbledash/web-component';
  import { onMount } from 'svelte';
  
  let dashboard;
  
  onMount(() => {
    dashboard.widgets = { default: createWidget };
    dashboard.initialLayout = { tiles: [...] };
  });
</script>

<ud-dashboard
  bind:this={dashboard}
  mode="resize"
  overlays
  keyboard
  on:ud-tile-click={(e) => console.log(e.detail.tileId)}
/>

See Also