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

@twinstudio/twin-editor-widgets

v0.1.1

Published

Reusable React widget components for building interactive dashboards and control panels

Readme

@twinstudio/twin-editor-widgets

Reusable React widget components for building interactive dashboards and control panels

npm version License TypeScript React

Overview

@twinstudio/twin-editor-widgets is a lightweight, framework-agnostic React component library designed for building interactive control panels and dashboards. Originally developed for TwinStudio's digital twin platform, these widgets provide a consistent UI for managing entity visibility, data visualization, and user interactions.

Key Features

  • 🎨 Consistent Design - Built with Tailwind CSS and shadcn/ui components
  • 🔌 Framework Agnostic - Works with Next.js, Vite, Create React App, and more
  • 📦 Tree Shakeable - Optimized bundle size with ESM and CJS builds
  • 🎯 TypeScript First - Full type definitions included
  • Accessible - WCAG AA compliant components
  • 🧪 Well Tested - Comprehensive test coverage

Installation

npm install @twinstudio/twin-editor-widgets
# or
yarn add @twinstudio/twin-editor-widgets
# or
pnpm add @twinstudio/twin-editor-widgets

Peer Dependencies

npm install react react-dom

Quick Start

import { ControlByNamePanel } from "@twinstudio/twin-editor-widgets";
import type { WidgetAction } from "@twinstudio/twin-editor-widgets";

function App() {
  const items = [
    {
      id: "entity-1",
      name: "Chiller 01",
      classUri: "brick:Chiller",
      type: "equipment",
      visibility: true,
    },
    {
      id: "entity-2",
      name: "AHU 01",
      classUri: "brick:AHU",
      type: "equipment",
      visibility: false,
    },
  ];

  const handleAction = (action: WidgetAction) => {
    switch (action.type) {
      case "visibility/toggle-batch":
        console.log("Toggle visibility:", action.payload);
        break;
      case "entity/select":
        console.log("Select entity:", action.payload.id);
        break;
      // Handle other actions...
    }
  };

  return (
    <ControlByNamePanel
      title="Equipment Control"
      items={items}
      onAction={handleAction}
    />
  );
}

Components

ControlByNamePanel

A control panel for managing entity visibility and selection with batch operations.

Features:

  • Toggle individual item visibility
  • Batch visibility operations
  • Isolate mode (show only one item)
  • Filter to show only visible items
  • Entity selection
  • Reset to default state

Props:

interface ControlByNamePanelProps {
  title?: string;
  description?: string;
  items: Array<string | ControlSpecificClassItem>;
  showExportButton?: boolean;
  onExport?: () => void;
  onAction?: (action: WidgetAction) => void;
  toolName?: string;
  toolCallId?: string;
}

Example:

<ControlByNamePanel
  title="Building Equipment"
  description="Control visibility of HVAC equipment"
  items={equipmentList}
  onAction={handleVisibilityChange}
/>

Styling

This package uses Tailwind CSS with semantic design tokens. You need to configure Tailwind in your project.

1. Install Tailwind CSS

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

2. Configure Tailwind

// tailwind.config.js
import widgetsPreset from "@twinstudio/twin-editor-widgets/tailwind.config";

export default {
  presets: [widgetsPreset],
  content: [
    "./src/**/*.{js,ts,jsx,tsx}",
    "./node_modules/@twinstudio/twin-editor-widgets/dist/**/*.{js,mjs}",
  ],
};

3. Add CSS Variables

/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 222.2 84% 4.9%;
    --primary: 222.2 47.4% 11.2%;
    --primary-foreground: 210 40% 98%;
    --secondary: 210 40% 96.1%;
    --secondary-foreground: 222.2 47.4% 11.2%;
    --muted: 210 40% 96.1%;
    --muted-foreground: 215.4 16.3% 46.9%;
    --accent: 210 40% 96.1%;
    --accent-foreground: 222.2 47.4% 11.2%;
    --border: 214.3 31.8% 91.4%;
    --input: 214.3 31.8% 91.4%;
    --ring: 222.2 84% 4.9%;
    --radius: 0.5rem;
  }

  .dark {
    --background: 222.2 84% 4.9%;
    --foreground: 210 40% 98%;
    --primary: 210 40% 98%;
    --primary-foreground: 222.2 47.4% 11.2%;
    --secondary: 217.2 32.6% 17.5%;
    --secondary-foreground: 210 40% 98%;
    --muted: 217.2 32.6% 17.5%;
    --muted-foreground: 215 20.2% 65.1%;
    --accent: 217.2 32.6% 17.5%;
    --accent-foreground: 210 40% 98%;
    --border: 217.2 32.6% 17.5%;
    --input: 217.2 32.6% 17.5%;
    --ring: 212.7 26.8% 83.9%;
  }
}

Framework Integration

Next.js

// next.config.js
const config = {
  transpilePackages: ["@twinstudio/twin-editor-widgets"],
};

export default config;

Vite

// vite.config.ts
import { defineConfig } from "vite";

export default defineConfig({
  optimizeDeps: {
    include: ["@twinstudio/twin-editor-widgets"],
  },
});

API Reference

Types

ControlSpecificClassItem

interface ControlSpecificClassItem {
  id: string;
  name: string;
  classUri?: string;
  type?: string;
  visibility: boolean;
}

WidgetAction

type WidgetAction =
  | {
      type: "visibility/reset";
      payload: Record<string, never>;
    }
  | {
      type: "visibility/toggle-batch";
      payload: {
        visibleIds: string[];
        hiddenIds: string[];
      };
    }
  | {
      type: "visibility/isolate";
      payload: { id: string; visibility: boolean } | null;
    }
  | {
      type: "visibility/show-only-visible";
      payload: { itemIds: string[] } | null;
    }
  | {
      type: "entity/select";
      payload: { id: string };
    };

Examples

Basic Usage

import { ControlByNamePanel } from "@twinstudio/twin-editor-widgets";

function BasicExample() {
  const items = [
    { id: "1", name: "Item 1", visibility: true },
    { id: "2", name: "Item 2", visibility: false },
  ];

  return <ControlByNamePanel items={items} />;
}

With Action Handling

import { ControlByNamePanel } from "@twinstudio/twin-editor-widgets";
import { useState } from "react";

function ActionExample() {
  const [items, setItems] = useState([
    { id: "1", name: "Item 1", visibility: true },
    { id: "2", name: "Item 2", visibility: false },
  ]);

  const handleAction = (action) => {
    if (action.type === "visibility/toggle-batch") {
      setItems((prev) =>
        prev.map((item) => ({
          ...item,
          visibility: action.payload.visibleIds.includes(item.id),
        }))
      );
    }
  };

  return <ControlByNamePanel items={items} onAction={handleAction} />;
}

With Export Button

import { ControlByNamePanel } from "@twinstudio/twin-editor-widgets";

function ExportExample() {
  const items = [
    { id: "1", name: "Item 1", visibility: true },
  ];

  const handleExport = () => {
    console.log("Exporting widget data:", items);
    // Send to dashboard, save to server, etc.
  };

  return (
    <ControlByNamePanel
      items={items}
      showExportButton
      onExport={handleExport}
    />
  );
}

Browser Support

  • Chrome/Edge (Chromium) - latest 2 versions
  • Firefox - latest 2 versions
  • Safari - latest 2 versions

Bundle Size

| Format | Size (gzipped) | |--------|----------------| | ESM | ~45 KB | | CJS | ~45 KB |

Development

# Install dependencies
pnpm install

# Build package
pnpm build

# Run tests
pnpm test

# Type check
pnpm check:type

# Lint
pnpm check:lint

Contributing

We welcome contributions! Please see our contributing guidelines for details.

Changelog

See CHANGELOG.md for version history and release notes.

License

MIT License - see LICENSE for details.

Support

Related Projects


Made with ❤️ by TwinStudio