@twinstudio/twin-editor-widgets
v0.1.1
Published
Reusable React widget components for building interactive dashboards and control panels
Maintainers
Readme
@twinstudio/twin-editor-widgets
Reusable React widget components for building interactive dashboards and control panels
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-widgetsPeer Dependencies
npm install react react-domQuick 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 -p2. 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:lintContributing
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
- TwinStudio - Digital twin platform
- shadcn/ui - UI component library
- Tailwind CSS - Utility-first CSS framework
Made with ❤️ by TwinStudio
