npm package: @prahladkuma11/canvas-editor
Description: Powerful React/Konva canvas editor with floating toolbars, multi-page support, and text effects
Author: Canvas Editor Contributors
Latest version: 1.0.9


# @prahladkuma11/canvas-editor

A production-ready React component library for building powerful canvas-based design applications. Complete with text effects, multi-page support, floating toolbars, Unsplash image integration, and role-based permissions.

**Version:** 1.0.7 · **License:** MIT

## 🚀 Features

✨ **Complete Design Tools**
- Text editing with 40+ Google Fonts
- Shape tools (rectangles, circles)
- Image editing with filters (hue, saturation, brightness, sharpness)
- Icon support via Iconify (10,000+ icons)

🎨 **Advanced Effects**
- Text effects (shadow, highlight, glitch, echo)
- Directional angle controls
- Image filters and adjustments
- Smart alignment guides during drag

📄 **Multi-Page Support**
- Sides mode (front/back pages)
- Pages mode (unlimited pages)
- Page management with admin controls
- Multiple printable areas per page

🔐 **Role-Based Permissions**
- Admin vs Custom user roles
- Feature access control
- Permission-based UI rendering

⚡ **High Performance**
- High-performance Konva rendering
- Lazy font loading
- Optimized asset management

📱 **Responsive Design**
- Mobile-friendly floating toolbars
- Adaptive UI based on screen size
- Touch-ready controls

🖼️ **Unsplash Integration**
- Browse and search millions of photos
- Built-in request caching (memory + localStorage)
- Pass your API key via `unsplashApiKey` prop

## 📦 Installation

```bash
npm install @prahladkuma11/canvas-editor
```

### Requirements
- React 18+ or 19+
- React DOM 18+ or 19+

> The library ships its own CSS. **Do not** add Tailwind CSS — it is not required.

## 🎯 Quick Start

### Option 1: Use Complete App Component

The simplest way — import the complete, ready-to-use canvas editor:

```jsx
import React from 'react';
import { CanvasEditorApp } from '@prahladkuma11/canvas-editor';
import '@prahladkuma11/canvas-editor/styles';

export default function MyApp() {
  return (
    <CanvasEditorApp
      designStateId="tshirt-design-001"
      userRole="admin"
      unsplashApiKey={import.meta.env.VITE_UNSPLASH_ACCESS_KEY}
      initialState={{
        settings: { showGrids: false },
      }}
      onChange={(state) => {
        // Live state updates
        console.log('Editor changed', state);
      }}
      onSave={(state) => {
        // Save snapshot from Export button
        localStorage.setItem('canvas-template', JSON.stringify(state));
      }}
    />
  );
}
```

This gives you a full-featured editor with all tools, panels, and controls.

### Option 2: Build Custom Layout

For more control, use individual components:

```jsx
import React from 'react';
import {
  CanvasEditorProvider,
  CanvasSurface,
  LeftPanel,
  RightPanel,
} from '@prahladkuma11/canvas-editor';
import '@prahladkuma11/canvas-editor/styles';

export default function CustomEditor() {
  return (
    <CanvasEditorProvider>
      <div style={{ display: 'flex', height: '100vh', width: '100vw' }}>
        <LeftPanel />
        <CanvasSurface />
        <RightPanel />
      </div>
    </CanvasEditorProvider>
  );
}
```

## 🔌 Core API



#### Props

| Prop | Type | Description |
|---|---|---|
| `userRole` | `'admin' \| 'custom'` | Actual user role. Custom users are restricted to custom view only. |
| `role` | `'admin' \| 'custom'` | Optional preview/view role. Admin users can switch between admin and custom views. |
| `unsplashApiKey` | `string` | Your Unsplash API access key. Pass this to enable the image browser in the Graphics and Background panels. |
| `designStateId` | `string` | Optional design identifier for automatic localStorage persistence. If local data exists for this ID and `initialState` is also provided, the editor prompts users to choose between local data and incoming design data. |
| `initialState` | `Partial<CanvasState> \| CanvasState` | Optional initial state to pre-populate the canvas. Missing fields are filled from library defaults (including nested settings/pan and partial objects/pages). |
| `onChange` | `(state: CanvasState) => void` | Optional callback fired whenever canvas state changes. |
| `onSave` | `(state: CanvasState) => void` | Optional callback fired when save is triggered (for example from Export button). |

### Design ID Persistence Example

Use `designStateId` when you want autosave/restore by design key:

```jsx
import { CanvasEditorApp } from '@prahladkuma11/canvas-editor';

export default function HostApp() {
  const designId = 'order-7843-front';
  const incomingDesign = {
    settings: { showGrids: false },
  };

  return (
    <CanvasEditorApp
      userRole="admin"
      designStateId={designId}
      initialState={incomingDesign}
      onSave={(state) => {
        // Optional: persist exported JSON to your backend
        console.log('Saved state', state);
      }}
    />
  );
}
```

Behavior:
- If localStorage has no data for this ID, `initialState` is used.
- If localStorage has data for this ID and `initialState` is passed, users are prompted to choose which one to load.
- State changes are auto-saved to localStorage under this ID.

### Admin Template + Custom Load Example

Save a template JSON as admin, then load it for custom users via `initialState`:

```jsx
import { useState } from 'react';
import { CanvasEditorApp } from '@prahladkuma11/canvas-editor';
import '@prahladkuma11/canvas-editor/styles';

export default function App() {
  const [savedTemplate, setSavedTemplate] = useState(() => {
    const raw = localStorage.getItem('canvas-template');
    return raw ? JSON.parse(raw) : undefined;
  });

  return (
    <>
      <CanvasEditorApp
        userRole="admin"
        initialState={savedTemplate}
        onSave={(state) => {
          const json = JSON.stringify(state);
          localStorage.setItem('canvas-template', json);
          setSavedTemplate(state);
        }}
      />

      <CanvasEditorApp
        userRole="custom"
        initialState={savedTemplate}
      />
    </>
  );
}
```

### useCanvasEditor

Access canvas state and dispatch actions:

```jsx
import { useCanvasEditor } from '@prahladkuma11/canvas-editor';

function MyComponent() {
  const { state, dispatch, triggerSave } = useCanvasEditor();

  // Add text object
  const addText = () => {
    dispatch({
      type: 'ADD_OBJECT',
      payload: {
        type: 'text',
        text: 'Hello Canvas',
        x: 100,
        y: 100,
        fontSize: 24,
        fontFamily: 'Poppins',
      },
    });
  };

  // Update object
  const updateText = () => {
    dispatch({
      type: 'UPDATE_OBJECT',
      payload: {
        id: 'object-id',
        updates: {
          text: 'Updated Text',
          fontSize: 32,
        },
      },
    });
  };

  // Delete object
  const deleteObject = (id) => {
    dispatch({
      type: 'DELETE_OBJECTS',
      payload: [id],
    });
  };

  const exportJson = () => {
    console.log(JSON.stringify(state, null, 2));
    triggerSave();
  };

  return (
    <div>
      <button onClick={addText}>Add Text</button>
      <button onClick={updateText}>Update Text</button>
      <button onClick={() => deleteObject('object-1')}>Delete</button>
      <button onClick={exportJson}>Export</button>
    </div>
  );
}
```

### Available Actions

```typescript
// Object management
dispatch({ type: 'ADD_OBJECT', payload: { type, x, y, ... } });
dispatch({ type: 'UPDATE_OBJECT', payload: { id, updates: { ...props } } });
dispatch({ type: 'DELETE_OBJECTS', payload: [objectId] });
dispatch({ type: 'SET_SELECTION', payload: [objectId] });
dispatch({ type: 'SET_EDITING_ID', payload: objectId });

// Canvas controls
dispatch({ type: 'SET_ZOOM', payload: zoomLevel });
dispatch({ type: 'SET_PAN', payload: { x, y } });
dispatch({ type: 'SET_TOOL', payload: 'select' | 'pan' | 'text' | 'rect' | 'circle' | 'image' });

// Page management
dispatch({ type: 'SET_PAGE_MODE', payload: 'sides' | 'pages' });
dispatch({ type: 'ADD_PAGE', payload: { name: 'Page Name', ... } });
dispatch({ type: 'DELETE_PAGE', payload: pageId });
dispatch({ type: 'SET_CURRENT_PAGE', payload: pageId });
dispatch({ type: 'SET_SIDE_ENABLED', payload: { side: 'front' | 'back', enabled: true } });

// Background/printable area
dispatch({ type: 'SET_BACKGROUND_IMAGE', payload: { image: imageDataUrl } });
dispatch({ type: 'SET_BACKGROUND_COLOR', payload: { color: '#ffffff' } });
dispatch({ type: 'TOGGLE_EDIT_PRINTABLE_AREA' });
dispatch({ type: 'ADD_PRINTABLE_AREA' });
dispatch({ type: 'DELETE_PRINTABLE_AREA', payload: printableAreaId });
dispatch({ type: 'SET_ACTIVE_PRINTABLE_AREA', payload: printableAreaId });
dispatch({ type: 'UPDATE_PRINTABLE_AREA', payload: { id: printableAreaId, x: 120, y: 80 } });

// Role & permissions
dispatch({ type: 'SET_ROLE', payload: 'admin' | 'custom' });
dispatch({ type: 'SET_USER_ROLE', payload: 'admin' | 'custom' });

// Settings
dispatch({ type: 'UPDATE_SETTINGS', payload: { showRulers: true, ... } });
```

## 📦 Components

### Main Components

#### CanvasSurface
Main canvas rendering area with interactive object selection and manipulation.

```jsx
import { CanvasSurface } from '@prahladkuma11/canvas-editor';

<CanvasSurface />
```

#### LeftPanel
Sidebar with page/side selection and tools.

```jsx
import { LeftPanel } from '@prahladkuma11/canvas-editor';

<LeftPanel />
```

#### RightPanel
Properties panel for editing selected object properties.

```jsx
import { RightPanel } from '@prahladkuma11/canvas-editor';

<RightPanel />
```

#### ProductSidePanel
Floating side panel with secondary tools.

```jsx
import { ProductSidePanel } from '@prahladkuma11/canvas-editor';

<ProductSidePanel />
```

### Floating Toolbars

#### FloatingObjectToolbar
Appears when hovering over objects for quick manipulation.

```jsx
import { FloatingObjectToolbar } from '@prahladkuma11/canvas-editor';

<FloatingObjectToolbar />
```

#### TextToolbar
Floating toolbar for text formatting (font, size, effects, alignment).

```jsx
import { TextToolbar } from '@prahladkuma11/canvas-editor';

<TextToolbar />
```

#### ShapeToolbar
Toolbar for shape properties (fill, stroke, rotation).

```jsx
import { ShapeToolbar } from '@prahladkuma11/canvas-editor';

<ShapeToolbar />
```

### Secondary Panels

#### SecondaryPanel
Tab-based panel container for advanced controls.

```jsx
import { SecondaryPanel } from '@prahladkuma11/canvas-editor';

<SecondaryPanel />
```

**Available sub-panels:**
- `TextPanel` - Text-specific controls
- `GraphicsPanel` - Shape and image tools
- `BackgroundPanel` - Page background settings
- `LayersPanel` - Object layer management
- `MaterialPanel` - Color and material presets
- `ObjectsPanel` - Object listing and selection
- `UploadsPanel` - Asset management

## 💾 Usage Examples

### Example 1: Add Text with Effects

```jsx
import { useCanvasEditor } from '@prahladkuma11/canvas-editor';

function AddEffectText() {
  const { dispatch } = useCanvasEditor();

  const addGlitchText = () => {
    dispatch({
      type: 'ADD_OBJECT',
      payload: {
        type: 'text',
        text: 'Glitch Effect',
        x: 100,
        y: 100,
        fontSize: 48,
        fontFamily: 'Bebas Neue',
        textEffect: 'glitch',
        effectColor: '#ff0000',
        effectDistance: 3,
        effectAngle: 45,
      },
    });
  };

  return <button onClick={addGlitchText}>Add Glitch Text</button>;
}
```

### Example 2: Work with Images

```jsx
function ImageEditor() {
  const { dispatch } = useCanvasEditor();

  const addImage = (imageUrl) => {
    dispatch({
      type: 'ADD_OBJECT',
      payload: {
        type: 'image',
        src: imageUrl,
        x: 50,
        y: 50,
        width: 300,
        height: 300,
      },
    });
  };

  const applyFilters = (imageId) => {
    dispatch({
      type: 'UPDATE_OBJECT',
      payload: {
        id: imageId,
        updates: {
          imageAdjustHue: 25,
          imageAdjustSaturation: 1.3,
          imageAdjustBrightness: 1.1,
          imageSharpness: 1.5,
        },
      },
    });
  };

  return (
    <>
      <button onClick={() => addImage('https://example.com/image.jpg')}>
        Add Image
      </button>
      <button onClick={() => applyFilters('image-id')}>Apply Filters</button>
    </>
  );
}
```

### Example 3: Multi-Page Management

```jsx
function PageManager() {
  const { state, dispatch } = useCanvasEditor();

  const switchToPages = () => {
    dispatch({ type: 'SET_PAGE_MODE', payload: 'pages' });
  };

  const addNewPage = () => {
    dispatch({
      type: 'ADD_PAGE',
      payload: { name: 'Page ' + (state.pages.length + 1) },
    });
  };

  const selectPage = (pageId) => {
    dispatch({ type: 'SET_CURRENT_PAGE', payload: pageId });
  };

  return (
    <div>
      <button onClick={switchToPages}>Switch to Pages Mode</button>
      <button onClick={addNewPage}>Add Page</button>
      <div>
        {state.pages.map((page) => (
          <button
            key={page.id}
            onClick={() => selectPage(page.id)}
            style={{
              fontWeight: page.id === state.currentPageId ? 'bold' : 'normal',
            }}
          >
            {page.name}
          </button>
        ))}
      </div>
    </div>
  );
}
```

### Example 4: Role-Based Features

```jsx
function RoleToggle() {
  const { state, dispatch } = useCanvasEditor();

  const toggleRole = () => {
    const newRole = state.role === 'admin' ? 'custom' : 'admin';
    dispatch({ type: 'SET_ROLE', payload: newRole });
  };

  return (
    <div>
      <p>Current Role: {state.role}</p>
      <button onClick={toggleRole}>Switch Role</button>
      {state.role === 'admin' && (
        <p>✅ Admin can add/remove pages and customize layouts</p>
      )}
      {state.role === 'custom' && (
        <p>⚠️ Custom users have limited permissions</p>
      )}
    </div>
  );
}
```

## 🎨 Fonts

The editor includes 40+ Google Fonts organized by category:

**Sans-serif:** Inter, Roboto, Poppins, Montserrat, Open Sans, Lato, Ubuntu, Source Sans Pro, Raleway, Nunito

**Serif:** Playfair Display, Merriweather, PT Serif, Crimson Text, Lora, Cormorant

**Monospace:** JetBrains Mono, Roboto Mono, IBM Plex Mono

**Display:** Bebas Neue, Oswald, Barlow Condensed, Fredoka

**Handwriting:** Caveat, Pacifico, Permanent Marker

Fonts are loaded dynamically on-demand via WebFont Loader.

```jsx
import { TEXT_FONT_OPTIONS, ensureFontLoaded } from '@prahladkuma11/canvas-editor';

// List all available fonts
console.log(TEXT_FONT_OPTIONS);

// Ensure a font is loaded before using it
await ensureFontLoaded('Poppins');
```

## 🖼️ Unsplash Integration

The Graphics and Background panels can browse and search Unsplash photos. To enable them, pass your Unsplash API access key via the `unsplashApiKey` prop:

```jsx
import { CanvasEditorProvider } from '@prahladkuma11/canvas-editor';

<CanvasEditorProvider unsplashApiKey={process.env.REACT_APP_UNSPLASH_KEY}>
  ...
</CanvasEditorProvider>
```

Or set the key programmatically (e.g. after fetching from your backend):

```js
import { setUnsplashApiKey } from '@prahladkuma11/canvas-editor';

setUnsplashApiKey('your_access_key');
```

To check if a key is currently set:

```js
import { hasUnsplashApiKey } from '@prahladkuma11/canvas-editor';

if (hasUnsplashApiKey()) {
  // images are available
}
```

If no key is provided, the image panels display an informational banner instead of broken UI.

Results are cached in memory and `localStorage` to minimize API calls.

## 🎯 Type Definitions

Full TypeScript support with exported types:

```typescript
import {
  CanvasState,
  CanvasObject,
  Page,
  Role,
  ToolType,
  PageMode,
  CanvasEditorContextType,
  CanvasAction,
} from '@prahladkuma11/canvas-editor';

interface CanvasState {
  objects: CanvasObject[];
  selectedObjectId: string | null;
  activeTool: ToolType;
  zoom: number;
  pan: { x: number; y: number };
  pages: Page[];
  selectedPageId: string;
  role: Role;
  settings: {
    showRulers: boolean;
    showGrids: boolean;
  };
}

interface CanvasObject {
  id: string;
  type: 'text' | 'rect' | 'circle' | 'image';
  x: number;
  y: number;
  width?: number;
  height?: number;
  // ... many more properties
}

type Role = 'admin' | 'custom';
type ToolType = 'select' | 'pan' | 'text' | 'rect' | 'circle' | 'image';
type PageMode = 'sides' | 'pages';
```

## 🎨 Styling

The library ships its own standalone CSS — **no Tailwind CSS required**. Simply import the stylesheet once at the top level of your app:

```js
import '@prahladkuma11/canvas-editor/styles';
```

This maps to `dist/canvas-editor.css` in the package. The stylesheet is self-contained and will not conflict with your existing CSS framework or reset.

## 🎮 Keyboard Shortcuts

- **Delete** - Delete selected object
- **Escape** - Deselect
- **Arrow Keys** - Move selected object
- **Shift + Arrow Keys** - Fine-tune position
- **V** - Select tool
- **H** - Pan tool

## ⚡ Performance Tips

1. **Lazy Font Loading** - Fonts load on-demand when used
2. **Asset Optimization** - Compress images before uploading
3. **Layer Management** - Use the Layers panel to organize objects
4. **Page Structure** - Keep pages focused on specific content
5. **Zoom Levels** - Use preset zoom levels (25%, 50%, 100%, 200%, etc.)

## 🌍 Browser Support

- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- Mobile browsers (iOS Safari, Chrome Mobile)

## 📱 Responsive Behavior

The library automatically adapts to different screen sizes:

- **Desktop (1920px+):** Full UI with all panels
- **Tablet (768px-1920px):** Optimized layout with hidden panels
- **Mobile (<768px):** Icon-only toolbars and collapsible panels

## 🔒 Permissions & Roles

### Admin Role
- Add/remove pages
- Customize layouts
- Set canvas background
- Adjust printable areas
- Export canvas

### Custom Role
- Edit objects
- Add text/shapes/images
- Apply effects
- Limited feature access

## 📊 State Management

The editor uses React Context + useReducer for state management:

```jsx
const { state, dispatch } = useCanvasEditor();

// state: Current canvas state
// dispatch: Send actions to update state
```

No external state management required!

## 📦 Exports Reference

```javascript
// Complete app
import { CanvasEditorApp } from '@prahladkuma11/canvas-editor';

// Core
import { CanvasEditorProvider, useCanvasEditor } from '@prahladkuma11/canvas-editor';

// Components
import {
  CanvasSurface, LeftPanel, RightPanel, SecondaryPanel,
  FloatingObjectToolbar, TextToolbar, ShapeToolbar,
} from '@prahladkuma11/canvas-editor';

// Unsplash utilities
import { setUnsplashApiKey, hasUnsplashApiKey } from '@prahladkuma11/canvas-editor';

// Font utilities
import { ensureFontLoaded, TEXT_FONT_OPTIONS } from '@prahladkuma11/canvas-editor';

// Types
import type { CanvasEditorAppProps, CanvasState, CanvasObject, Page, Role, ToolType } from '@prahladkuma11/canvas-editor';
```

## 🚀 Distribution

The package exports both ES Module and CommonJS bundles plus a stylesheet:

```json
{
  "exports": {
    ".": {
      "import": "./dist/canvas-editor.mjs",
      "require": "./dist/canvas-editor.cjs"
    },
    "./styles": "./dist/canvas-editor.css"
  }
}
```

### Usage in different environments

```javascript
// ES Module / TypeScript
import { CanvasEditorApp, useCanvasEditor } from '@prahladkuma11/canvas-editor';

// CommonJS
const { CanvasEditorApp } = require('@prahladkuma11/canvas-editor');
```

## 🤝 Contributing

Contributions are welcome! Please fork the repository and submit a pull request.

## 📄 License

MIT License — See LICENSE file for details

---

Built with ❤️ using React, Konva, and TypeScript