Skip to content

React TypeScript library for building interactive dashboard grids. Features drag-and-drop widgets, automatic collision detection, responsive design, and a streamlined API for adding custom widget types. Perfect for admin panels, dashboards, and customizable layouts.

Notifications You must be signed in to change notification settings

TheRPMko/gridtech-react

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

6 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

GridTech React

A powerful, feature-rich drag-and-drop widget grid system for React applications. Built with TypeScript, performance optimized, and production-ready.

✨ Features

  • 🎯 Responsive Grid: Automatically scales to fit any screen size
  • πŸ–±οΈ Drag & Drop: Intuitive widget positioning with collision detection and reflow
  • 🧩 Custom Widgets: Easy integration of any React component
  • πŸ“ Flexible Layout: Configurable grid dimensions and widget sizes
  • 🎨 Themeable: Customizable styling with CSS variables
  • ⚑ Performance: Optimized for smooth interactions with throttled events
  • πŸ” Group Filtering: Show/hide groups of widgets
  • πŸ”„ Undo/Redo: Full history management with configurable limits
  • πŸ“‹ Multi-Selection: Select and manipulate multiple widgets at once
  • πŸš€ Bulk Operations: Move, resize, and delete multiple widgets simultaneously
  • πŸ“¦ Templates: Save and reuse widget arrangements
  • πŸ”— Widget Linking: Create relationships between widgets
  • 🎯 Smart Layout: Auto-arrange with multiple strategies (compact, distribute, align)
  • πŸ’Ύ Import/Export: Save and restore entire grid states
  • πŸ“Š Grid Analytics: Built-in metrics and occupancy tracking
  • 🌐 SSR Compatible: Works with Next.js, Gatsby, and other SSR frameworks

Installation

npm install gridtech-react

Quick Start

import React, { useState } from "react";
import { WidgetGrid, WidgetState, useWidgetActions } from "gridtech-react";
import "gridtech-react/dist/index.css";

// Define your custom widgets
const MyWidget = ({ title }: { title: string }) => (
  <div style={{ padding: "1rem" }}>
    <h3>{title}</h3>
    <p>This is a custom widget!</p>
  </div>
);

const widgetRenderers = {
  custom: MyWidget,
};

function App() {
  const [widgets, setWidgets] = useState<WidgetState[]>([
    {
      id: "widget-1",
      type: "custom",
      x: 0,
      y: 0,
      width: 4,
      height: 3,
      props: { title: "Hello World" },
    },
  ]);

  return (
    <WidgetGrid
      cols={24}
      rows={12}
      initialWidgets={widgets}
      onWidgetsChange={setWidgets}
      widgetRenderers={widgetRenderers}
      preventOverlap={true}
      defaultEditMode={true}
      showControls={true}
    />
  );
}

πŸš€ Advanced Features

Multi-Selection & Bulk Operations

function AdvancedGridDemo() {
  const {
    widgets,
    selectedWidgets,
    selectWidget,
    selectMultiple,
    selectAll,
    bulkMoveWidgets,
    bulkDeleteWidgets,
    undo,
    redo,
    canUndo,
    canRedo,
  } = useWidgetActions({
    cols: 24,
    rows: 12,
    preventOverlap: true,
    defaultWidgetSize: { w: 4, h: 3 },
  });

  return (
    <div>
      <div className="toolbar">
        <button onClick={() => selectAll()}>Select All</button>
        <button onClick={() => bulkMoveWidgets(selectedWidgets, 1, 0)}>
          Move Right
        </button>
        <button onClick={() => bulkDeleteWidgets(selectedWidgets)}>
          Delete Selected
        </button>
        <button onClick={undo} disabled={!canUndo}>
          Undo
        </button>
        <button onClick={redo} disabled={!canRedo}>
          Redo
        </button>
      </div>

      <WidgetGrid
        cols={24}
        rows={12}
        initialWidgets={widgets}
        selectedWidgets={selectedWidgets}
        onWidgetSelect={selectWidget}
        widgetRenderers={widgetRenderers}
        preventOverlap={true}
      />
    </div>
  );
}

Templates & Smart Layout

function TemplateDemo() {
  const {
    widgets,
    saveAsTemplate,
    applyTemplate,
    getAvailableTemplates,
    autoArrangeWidgets,
    exportGridState,
    importGridState,
  } = useWidgetActions({
    cols: 24,
    rows: 12,
    preventOverlap: true,
    defaultWidgetSize: { w: 4, h: 3 },
  });

  const handleSaveTemplate = () => {
    const selectedIds = widgets.slice(0, 3).map((w) => w.id); // First 3 widgets
    saveAsTemplate(selectedIds, "Dashboard Layout");
  };

  const handleAutoArrange = (strategy: "compact" | "distribute" | "align") => {
    autoArrangeWidgets(strategy);
  };

  return (
    <div>
      <div className="template-controls">
        <button onClick={handleSaveTemplate}>Save as Template</button>
        <button onClick={() => applyTemplate("Dashboard Layout")}>
          Apply Template
        </button>
        <button onClick={() => handleAutoArrange("compact")}>
          Compact Layout
        </button>
        <button onClick={() => handleAutoArrange("distribute")}>
          Distribute Evenly
        </button>
      </div>

      <WidgetGrid
        cols={24}
        rows={12}
        initialWidgets={widgets}
        widgetRenderers={widgetRenderers}
        preventOverlap={true}
      />
    </div>
  );
}

Widget Relationships & Links

function LinkedWidgetsDemo() {
  const {
    widgets,
    linkWidgets,
    unlinkWidgets,
    getLinkedWidgets
  } = useWidgetActions({
    cols: 24,
    rows: 12,
    preventOverlap: true,
    defaultWidgetSize: { w: 4, h: 3 }
  });

  const handleLinkWidgets = (sourceId: string, targetId: string) => {
    linkWidgets(sourceId, targetId, 'data-flow');
  };

  const getWidgetConnections = (widgetId: string) => {
    return getLinkedWidgets(widgetId);
  };

  return (
    <WidgetGrid
      cols={24}
      rows={12}
      initialWidgets={widgets}
      onWidgetLink={handleLinkWidgets}
      widgetRenderers={widgetRenderers}
      renderConnections={(widgetId) => (
        <ConnectionVisualization
          connections={getWidgetConnections(widgetId)}
        />

      )}
    />
  );
}

πŸ”§ Custom Hooks & Integration

Custom Hooks

import { useWidgetActions, useResponsiveGrid } from 'gridtech-react';

const MyCustomGrid = () => {
const { widgets, addWidget, moveWidget } = useWidgetActions({
cols: 24,
rows: 12,
preventOverlap: true,
defaultWidgetSize: { w: 4, h: 3 }
});

const { cellWidth, cellHeight } = useResponsiveGrid({
cols: 24,
rows: 12
});

return (
<div>
<button onClick={() => addWidget('custom', { title: 'New Widget' })}>
Add Widget
</button>
{/_ Your custom grid implementation _/}
</div>
);
};

API Reference

WidgetGrid Props

Prop Type Default Description
cols number 24 Number of grid columns
rows number 12 Number of grid rows
initialWidgets WidgetState[] [] Array of widget configurations
onWidgetsChange (widgets: WidgetState[]) => void - Callback when widgets change
onSelectionChange (selectedIds: string[]) => void - Callback when selection changes
widgetRenderers { [type: string]: ComponentType } - Map of widget types to components
preventOverlap boolean false Prevent widgets from overlapping
defaultEditMode boolean false Start in edit mode
groupFilters GroupFilter[] [] Array of group visibility filters
onGroupFiltersChange (filters: GroupFilter[]) => void - Callback when group filters change
enableHoverToAdd boolean false Enable hover-to-add widget functionality
availableWidgets AvailableWidget[] [] Available widget types for hover-to-add
interactionModes InteractionModes {} Control which interactions are enabled

useWidgetActions Hook

The enhanced useWidgetActions hook provides comprehensive grid management:

const {
  // Core state
  widgets,
  selectedWidgets,
  templates,
  links,

  // History management
  undo,
  redo,
  canUndo,
  canRedo,

  // Selection management
  selectWidget,
  selectMultiple,
  selectAll,
  clearSelection,

  // Core operations
  addWidget,
  moveWidget,
  resizeWidget,
  deleteWidget,

  // Bulk operations
  bulkMoveWidgets,
  bulkDeleteWidgets,
  bulkResizeWidgets,

  // Grid state management
  exportGridState,
  importGridState,
  clearGrid,
  getGridMetrics,

  // Template management
  saveAsTemplate,
  applyTemplate,
  getAvailableTemplates,

  // Advanced layout
  autoArrangeWidgets,
  findOptimalPosition,
  swapWidgets,

  // Widget relationships
  linkWidgets,
  unlinkWidgets,
  getLinkedWidgets
} = useWidgetActions({
  cols: 24,
  rows: 12,
  preventOverlap: true,
  defaultWidgetSize: { w: 4, h: 3 },
  maxHistorySize: 50,
  onWidgetsChange,
  onSelectionChange
});

Core Interfaces

WidgetState

interface WidgetState {
  id: string;
  type: string;
  x: number;
  y: number;
  width: number;
  height: number;
  props?: Record<string, any>;
  groupId?: string;
  minW?: number;
  minH?: number;
  maxW?: number;
  maxH?: number;
}

GridTemplate

interface GridTemplate {
  id: string;
  name: string;
  widgets: Omit<WidgetState, "id">[];
  createdAt: number;
}

WidgetLink

interface WidgetLink {
  sourceId: string;
  targetId: string;
  linkType: string;
}

GridExport

interface GridExport {
  widgets: WidgetState[];
  templates: GridTemplate[];
  links: WidgetLink[];
  metadata: {
    version: string;
    exportedAt: number;
    gridDimensions: { cols: number; rows: number };
  };
}

GridMetrics

interface GridMetrics {
  totalWidgets: number;
  occupancy: number; // 0-1 representing percentage of grid occupied
  averageWidgetSize: { width: number; height: number };
  largestWidget: { width: number; height: number };
  emptySpaces: number;
}

GroupFilter

interface GroupFilter {
  groupId: string;
  visible: boolean;
}

🎯 Key Methods Reference

Selection Methods

  • selectWidget(id, addToSelection?) - Select single widget, optionally add to current selection
  • selectMultiple(ids) - Select multiple widgets by ID array
  • selectAll() - Select all widgets in the grid
  • clearSelection() - Clear current selection

Bulk Operations

  • bulkMoveWidgets(widgetIds, deltaX, deltaY) - Move multiple widgets by offset
  • bulkDeleteWidgets(widgetIds) - Delete multiple widgets at once
  • bulkResizeWidgets(widgetIds, scale) - Scale multiple widgets by factor

History Management

  • undo() - Undo last action (configurable history size)
  • redo() - Redo last undone action
  • canUndo / canRedo - Boolean flags for UI state

Layout Intelligence

  • autoArrangeWidgets('compact' | 'distribute' | 'align') - Smart auto-arrangement
  • findOptimalPosition(width, height, preferences?) - Find best position for new widget
  • swapWidgets(id1, id2) - Exchange positions of two widgets

Templates & Persistence

  • saveAsTemplate(widgetIds, templateName) - Save widget arrangement as template
  • applyTemplate(templateName, position?) - Apply saved template to grid
  • exportGridState() - Export complete grid state for persistence
  • importGridState(state) - Restore grid from exported state

Analytics & Metrics

  • getGridMetrics() - Get occupancy, widget counts, and size analytics

πŸ”§ Error Handling

GridTech React includes built-in error handling with console warnings for common scenarios:

// All errors are logged with 'GridTech:' prefix for easy filtering
// - No space available for new widgets
// - Cannot move widget due to space constraints
// - Cannot resize widget due to space constraints

// Monitor errors in production:
const originalWarn = console.warn;
console.warn = function (...args: any[]) {
  if (args[0]?.includes("GridTech:")) {
    // Send to your error tracking service
    analytics.track("gridtech_warning", { message: args[0] });
  }
  originalWarn.apply(console, args);
};

🌐 SSR Compatibility

GridTech React is fully compatible with server-side rendering frameworks:

// Next.js example
import dynamic from "next/dynamic";

const WidgetGrid = dynamic(
  () => import("gridtech-react").then((mod) => mod.WidgetGrid),
  { ssr: false }
);

⚑ Performance Tips

  1. Large Grids: Use preventOverlap: false for grids with 100+ widgets
  2. History Size: Adjust maxHistorySize based on memory constraints
  3. Throttling: Mouse events are already throttled for optimal performance
  4. Memoization: Widget renderers should be memoized with React.memo()
const MemoizedWidget = React.memo(({ title, data }: WidgetProps) => {
  return (
    <div>
      <h3>{title}</h3>
      <ComplexVisualization data={data} />
    </div>
  );
});

Advanced Usage

Custom Hooks

import { useWidgetActions, useResponsiveGrid } from "gridtech-react";

const MyCustomGrid = () => {
  const { widgets, addWidget, moveWidget } = useWidgetActions({
    cols: 24,
    rows: 12,
    preventOverlap: true,
    defaultWidgetSize: { w: 4, h: 3 },
  });

  const { cellWidth, cellHeight } = useResponsiveGrid({
    cols: 24,
    rows: 12,
  });

  return (
    <div>
      <button onClick={() => addWidget("custom", { title: "New Widget" })}>
        Add Widget
      </button>
      {/* Your custom grid implementation */}
    </div>
  );
};

Group Filtering

GridTech React supports simple group filtering - perfect for organizing widgets by category:

import React, { useState, useRef } from "react";
import {
  WidgetGrid,
  WidgetState,
  GroupFilter,
  WidgetGridRef,
} from "gridtech-react";

function App() {
  const gridRef = useRef<WidgetGridRef>(null);
  const [widgets, setWidgets] = useState<WidgetState[]>([
    {
      id: "chart-1",
      type: "chart",
      x: 0,
      y: 0,
      width: 4,
      height: 3,
      groupId: "analytics", // Assign to group
      props: { title: "Sales Chart" },
    },
    {
      id: "metric-1",
      type: "metric",
      x: 4,
      y: 0,
      width: 2,
      height: 2,
      groupId: "kpi", // Different group
      props: { value: 1234, label: "Total Sales" },
    },
  ]);

  const [groupFilters, setGroupFilters] = useState<GroupFilter[]>([]);

  // Hide/show groups
  const toggleGroup = (groupId: string, visible: boolean) => {
    gridRef.current?.setGroupVisible(groupId, visible);
  };

  return (
    <div>
      <button onClick={() => toggleGroup("analytics", false)}>
        Hide Analytics
      </button>
      <button onClick={() => toggleGroup("kpi", false)}>Hide KPIs</button>

      <WidgetGrid
        ref={gridRef}
        cols={24}
        rows={12}
        initialWidgets={widgets}
        onWidgetsChange={setWidgets}
        groupFilters={groupFilters}
        onGroupFiltersChange={setGroupFilters}
        widgetRenderers={widgetRenderers}
      />
    </div>
  );
}

πŸ“– Full Group Filtering Documentation

Migration Guide

From 0.4.x to 0.5.0

Version 0.5.0 maintains full backward compatibility while adding powerful new features:

// Existing code continues to work unchanged
const { widgets, moveWidget, addWidget, deleteWidget } = useWidgetActions({
  cols: 12,
  rows: 8,
  preventOverlap: true,
  defaultWidgetSize: { w: 4, h: 3 },
});

// New features are opt-in
const {
  // Existing API (unchanged)
  widgets,
  moveWidget,
  addWidget,
  deleteWidget,
  // New history features
  undo,
  redo,
  canUndo,
  canRedo,
  // New selection features
  selectedWidgets,
  selectWidget,
  clearSelection,
  // New bulk operations
  bulkMoveWidgets,
  bulkDeleteWidgets,
  // New templates
  saveAsTemplate,
  applyTemplate,
  getAvailableTemplates,
  // New linking
  linkWidgets,
  unlinkWidgets,
} = useWidgetActions({
  cols: 12,
  rows: 8,
  preventOverlap: true,
  defaultWidgetSize: { w: 4, h: 3 },
  // New optional configuration
  maxHistorySize: 50,
  onWidgetsChange,
  onSelectionChange,
});

Performance Tips

  1. Memoize grid options to prevent unnecessary re-renders:
const gridOptions = useMemo(
  () => ({
    cols: 12,
    rows: 8,
    gap: 8,
  }),
  []
);
  1. Use selection sparingly for large grids (>100 widgets)
  2. Limit history size for memory-conscious applications:
const actions = useWidgetActions({
  cols: 24,
  rows: 12,
  preventOverlap: true,
  maxHistorySize: 20, // Default is 50
});
  1. Template management - Clear unused templates periodically:
// Clear all templates
actions.clearAllTemplates(); // Method to clear templates

Styling

GridTech uses CSS custom properties for easy theming:

:root {
  --grid-background: #f5f5f5;
  --grid-border: #e0e0e0;
  --widget-background: white;
  --widget-border: #ddd;
  --widget-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  --widget-selected: #007acc;
  --widget-linked: #22c55e;
}

Changelog

v0.5.0 (Latest)

  • ✨ NEW: Undo/Redo functionality with configurable history
  • ✨ NEW: Multi-widget selection and bulk operations
  • ✨ NEW: Template system for saving/loading layouts
  • ✨ NEW: Widget linking with constraint management
  • ✨ NEW: Smart auto-arrangement algorithms
  • ✨ NEW: Grid analytics and export capabilities
  • ✨ NEW: Enhanced TypeScript definitions
  • πŸ”§ Improved performance and memory management
  • πŸ“– Comprehensive documentation updates

v0.4.x

  • Basic grid functionality
  • Widget drag & drop
  • Responsive breakpoints
  • Group filtering
  • CSS customization

License

MIT

About

React TypeScript library for building interactive dashboard grids. Features drag-and-drop widgets, automatic collision detection, responsive design, and a streamlined API for adding custom widget types. Perfect for admin panels, dashboards, and customizable layouts.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published