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

labellife-design-tool

v1.3.9

Published

Professional canvas editor built with React, TypeScript, and Konva

Readme

LabelLife Design Tool

Professional canvas editor built with React, TypeScript, and Konva.js. A powerful design tool similar to Canva/Polotno for creating graphics, posters, and designs.

Features

  • 🎨 Rich Canvas Editor - Full-featured design editor with text, images, shapes, and more
  • 📐 Multi-page Support - Create designs with multiple pages
  • 🖼️ Image Management - Upload images, use Unsplash, crop, mask, and apply filters
  • ✏️ Text Editing - Rich text editing with fonts, colors, alignment, and styling
  • 🔷 Shapes & Elements - Add various shapes (rectangles, circles, stars, polygons, etc.)
  • 🎭 Image Masking - Apply masks to images (circle, star, triangle, etc.)
  • 📤 Export Options - Export to PNG, JPG, or JSON
  • 📥 Template Import - Import JSON templates with user input collection
  • 🌐 i18n Support - Internationalization support (English, Dutch)
  • 🧩 Customizable UI - Fully customizable navbar with support for custom sections
  • ⚙️ Configurable - Customize features, panels, and behavior

Installation

npm install labellife-design-tool
# or
yarn add labellife-design-tool
# or
pnpm add labellife-design-tool

Usage

Basic Usage

import { CanvasEditor } from 'labellife-design-tool';
import 'labellife-design-tool/styles'; // Import CSS

function App() {
  return (
    <CanvasEditor
      name="My Design Editor"
      config={{
        export: {
          png: true,
          jpg: true,
          json: true,
        },
        multiPage: true,
        variables: true,
      }}
    />
  );
}

Note on CSS: The CSS is pre-processed and ready to use. Simply import it - no Tailwind configuration needed!

Panel Configuration (Built-in + Custom)

Control which panels appear in the left sidebar and their order using a single list via config.panels. You can mix built-in panel ids and custom panel objects in one array.

Available Built-in Panels

| Panel ID | Description | |---|---| | "text" | Add and edit text elements | | "elements" | Add shapes (rect, circle, star, polygon, etc.) | | "image" | Upload images, use Unsplash, crop/mask/filter | | "design" | Canvas size, presets, design settings | | "background" | Page background color/image | | "export" | Export to PNG/JPG/JSON (requires config.export) | | "variables" | Template variables (requires config.variables) |

Show Only Selected Built-in Panels

Omit any panel id to hide it. The order you list them is the order they appear:

<CanvasEditor
  name="Limited Editor"
  config={{
    panels: ["text", "elements", "image", "background"],
  }}
/>

This hides Design, Export, and Variables — only Text, Elements, Images, and Background are shown.

Add a Custom Panel

Create your own panel component and include it in the same list:

import { Sparkles } from "lucide-react";

// Your custom panel component — receives whatever props you pass
const MyAssetsPanel = ({ category }: { category: string }) => {
  return (
    <div style={{ padding: 16, color: "white" }}>
      <h3>My Assets</h3>
      <p>Showing assets for: {category}</p>
    </div>
  );
};

function App() {
  return (
    <CanvasEditor
      name="Custom Panel Editor"
      config={{
        export: { png: true, jpg: true, json: true },
        panels: [
          "text",
          "elements",
          {
            id: "my-assets",
            title: "My Assets",
            tooltip: "Browse my assets",
            icon: <Sparkles className="w-5 h-5" />,
            component: MyAssetsPanel,
            props: { category: "stickers" },
          },
          "image",
          "background",
          "export",
        ],
      }}
    />
  );
}

Custom Panel Definition

interface CustomPanelDefinition {
  id: string;                              // Unique panel id
  title: string;                           // Panel title
  tooltip?: string;                        // Tooltip on hover (defaults to title)
  icon: React.ReactElement;                // Icon shown in the left sidebar
  component: React.ComponentType<any>;     // Your panel React component
  props?: Record<string, any>;             // Props passed to your component
  actionType?: "setPanel" | "setToolAndPanel"; // Click behavior (default: "setPanel")
  toolValue?: ToolType;                    // Tool to activate (if actionType is "setToolAndPanel")
}

Add Elements from a Custom Panel

Option 1: Use standalone store (Polotno-like - recommended)

Create a standalone store that you can use anywhere:

import { createSimpleStore } from 'labellife-design-tool';

// Create store instance
const store = createSimpleStore({
  name: "My Design",
  width: 800,
  height: 600,
});

// Use store anywhere
store.addPage();

// Add elements - both APIs work
store.addElement({
  type: "image",
  src: "https://example.com/img.jpg",
  x: 100,
  y: 100,
  width: 200,
  height: 200,
});

// Or use Polotno-like API
store.activePage.addElement({
  type: "image",
  src: "https://example.com/img.jpg",
  x: 100,
  y: 100,
  width: 200,
  height: 200,
});

// Resize canvas
store.setSize(1200, 800);

// Load templates
await store.loadJSON(templateData);

// Export to blob
const pngBlob = await store.toBlob('png');
const jpgBlob = await store.toBlob('jpg');

// Download the blob
const downloadBlob = (blob: Blob, filename: string) => {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
  URL.revokeObjectURL(url);
};

downloadBlob(pngBlob, 'design.png');
downloadBlob(jpgBlob, 'design.jpg');

// Use with CanvasEditor (optional)
<CanvasEditor 
  name="Editor" 
  store={store}  // Pass the store (optional)
  config={{ panels: ["text", "elements"] }} 
/>

// Export store for use across your app
export const store = createSimpleStore();

Backward Compatibility

If you don't pass a store prop, CanvasEditor will create one internally:

// This still works - creates internal store
<CanvasEditor name="Editor" config={{ panels: ["text", "elements"] }} />

Option 2: Use the useCanvasStore hook (inside CanvasEditor only)

Any component inside CanvasEditor can access the store using the hook:

import { useCanvasStore } from 'labellife-design-tool';

const MyComponent = () => {
  const store = useCanvasStore();

  const handleAddImage = () => {
    store.activePage.addElement({
      type: "image",
      src: "https://example.com/img.jpg",
      x: 100,
      y: 100,
      width: 200,
      height: 200,
    });
  };

  const handleLoadTemplate = async () => {
    try {
      const templateData = {
        name: "My Template",
        width: 800,
        height: 600,
        pages: [
          {
            id: "page-1",
            name: "Page 1",
            elements: [
              {
                type: "text",
                text: "Hello World",
                x: 100,
                y: 100,
                fontSize: 24,
              },
            ],
          },
        ],
      };
      
      await store.loadJSON(templateData);
      console.log("Template loaded successfully!");
    } catch (error) {
      console.error("Failed to load template:", error);
    }
  };

  return (
    <div style={{ color: "white", padding: 16 }}>
      <p>Current page: {store.activePage.id}</p>
      <p>Canvas size: {store.width} × {store.height}</p>
      <button onClick={handleAddImage}>Add Image</button>
      <button onClick={handleLoadTemplate}>Load Template</button>
    </div>
  );
};

Option 2: Panel prop approach

Every panel (built-in or custom) also receives a store prop with a Polotno-like API:

// Your custom panel component
const MyImagesPanel = ({ store }) => {
  const handleAddImage = (url: string) => {
    store.activePage?.addElement({
      type: "image",
      src: url,
      x: 100,
      y: 100,
      width: 200,
      height: 200,
    });
  };

  return (
    <div style={{ color: "white", padding: 16 }}>
      <h3>My Images</h3>
      <button onClick={() => handleAddImage("https://example.com/img.jpg")}>
        Add Image
      </button>
      <p>Canvas size: {store.width} × {store.height}</p>
    </div>
  );
};

// Register the panel
<CanvasEditor
  config={{
    panels: [
      "text",
      "elements",
      { id: "my-images", title: "My Images", icon: <MyIcon />, component: MyImagesPanel },
      "image",
      "background",
    ],
  }}
/>

SimpleStore interface (Standalone)

interface SimpleStore {
  // Design properties
  design: CanvasDesign;
  width: number;
  height: number;
  
  // Page operations
  activePage: (Page & { addElement: (element: Partial<CanvasElement>) => void }) | null;
  activePageId: string;
  addPage(page?: Partial<Page>): string;
  deletePages(pageIds: string[]): void;
  setActivePage(pageId: string): void;
  
  // Element operations
  addElement(element: Partial<CanvasElement>): void;
  updateElement(elementId: string, updates: Partial<CanvasElement>): void;
  deleteElement(elementId: string): void;
  
  // Panel operations
  openSidePanel(panelId: string | null): void;
  activePanelId: string | null;
  
  // Design operations
  setDesign(design: CanvasDesign): void;
  updateDesign(updates: Partial<CanvasDesign>): void;
  setSize(width: number, height: number): void;
  loadJSON(jsonData: any): Promise<void>;
  
  // Export operations
  toBlob(type?: 'png' | 'jpg'): Promise<Blob>;
  
  // Events
  on(event: 'designChanged' | 'activePageChanged' | 'activePanelChanged', callback: Function): void;
  off(event: string): void;
}

Open panels programmatically

const MyPanel = ({ store }) => {
  const openTextPanel = () => store.openSidePanel("text");
  const openCustomPanel = () => store.openSidePanel("my-custom-panel");
  const closeAllPanels = () => store.openSidePanel(null);
  const tryOpenUnknown = () => store.openSidePanel("nonexistent"); // closes all panels

  const deletePage1 = () => store.deletePages(["page-1"]);
  const deleteMultiplePages = () => store.deletePages(["page-1", "page-2", "page-3"]);

  return (
    <div style={{ color: "white", padding: 16 }}>
      <p>Current page ID: {store.activePage.id}</p>
      <p>Canvas size: {store.width} × {store.height}</p>
      
      <button onClick={openTextPanel}>Open Text Panel</button>
      <button onClick={openCustomPanel}>Open Custom Panel</button>
      <button onClick={closeAllPanels}>Close All Panels</button>
      <button onClick={tryOpenUnknown}>Try Unknown Panel (closes all)</button>
      
      <button onClick={deletePage1}>Delete Page 1</button>
      <button onClick={deleteMultiplePages}>Delete Multiple Pages</button>
    </div>
  );
};

Default Behavior

If config.panels is not provided, the editor shows the default set:

  • text, elements, image, design, background
  • Plus variables if config.variables is set
  • Plus export if config.export is set

Configure Unsplash API Key

import { CanvasEditor, setUnsplashAccessKey } from 'labellife-design-tool';

// Set Unsplash API key programmatically
setUnsplashAccessKey('your-unsplash-access-key');

// Or use environment variable
// UNSPLASH_ACCESS_KEY=your-key npm start

Using Refs with CanvasEditor

The CanvasEditor component supports React refs, allowing you to access internal methods and properties:

import { useRef } from 'react';
import { CanvasEditor, CanvasEditorRef } from 'labellife-design-tool';

function MyEditor() {
  const editorRef = useRef<CanvasEditorRef>(null);
  
  const handleExport = () => {
    // Access methods via the ref
    if (editorRef.current) {
      // Export to PNG
      editorRef.current.exportToPNG();
      
      // Or get the current design
      const currentDesign = editorRef.current.getDesign();
      console.log('Current design:', currentDesign);
      
      // Access the Konva Stage directly
      const stage = editorRef.current.stage;
      // Do something with the stage...
    }
  };
  
  return (
    <div>
      <CanvasEditor
        ref={editorRef}
        name="Editor with Ref"
        config={{
          export: { png: true, jpg: true, json: true },
          multiPage: true
        }}
      />
      <button onClick={handleExport}>Export</button>
    </div>
  );
}

The CanvasEditorRef interface provides the following properties and methods:

  • stage: Direct access to the Konva.Stage instance
  • exportToPNG(): Export the canvas to a PNG file
  • exportToJPG(): Export the canvas to a JPG file
  • exportToJSON(): Export the design to a JSON file
  • getDesign(): Get the current design object
  • setCanvasSize(width, height): Set the canvas dimensions programmatically
  • loadDesign(jsonData): Load a design from JSON data (Promise-based)

Export Helpers (JSON object + Base64)

In addition to the UI download helpers, the library also provides functions that return data directly (no file download):

import { exportToJSONObject, canvasToDataURL } from 'labellife-design-tool';

Get a JSON-safe design object

const designObject = exportToJSONObject(editorRef.current.getDesign());
// designObject is a plain JSON-compatible object you can send to an API

Get a base64 data URL of the canvas

const stage = editorRef.current.stage;
if (stage) {
  const dataUrl = canvasToDataURL(stage, 'png', { pixelRatio: 2 });
  // dataUrl is like: "data:image/png;base64,iVBORw0..."
}

Canvas Size Management

The setCanvasSize method allows you to programmatically change the canvas dimensions. This is useful for responsive designs, template switching, or custom size inputs.

import { useRef } from 'react';
import { CanvasEditor, CanvasEditorRef } from 'labellife-design-tool';

function CanvasSizeExample() {
  const editorRef = useRef<CanvasEditorRef>(null);
  
  const handleSizeChange = (width: number, height: number) => {
    if (editorRef.current) {
      // Set canvas to custom dimensions
      editorRef.current.setCanvasSize(width, height);
    }
  };
  
  const presetSizes = [
    { name: 'Instagram Post', width: 1080, height: 1080 },
    { name: 'Instagram Story', width: 1080, height: 1920 },
    { name: 'Facebook Post', width: 1200, height: 630 },
    { name: 'YouTube Thumbnail', width: 1280, height: 720 },
  ];
  
  return (
    <div>
      <CanvasEditor
        ref={editorRef}
        name="Resizable Canvas"
        config={{
          export: { png: true, jpg: true, json: true },
        }}
      />
      
      <div className="size-controls">
        <h3>Canvas Size Controls</h3>
        
        {/* Preset sizes */}
        <div>
          <h4>Preset Sizes:</h4>
          {presetSizes.map((preset) => (
            <button
              key={preset.name}
              onClick={() => handleSizeChange(preset.width, preset.height)}
            >
              {preset.name} ({preset.width}x{preset.height})
            </button>
          ))}
        </div>
        
        {/* Custom size input */}
        <div>
          <h4>Custom Size:</h4>
          <input
            type="number"
            placeholder="Width"
            id="custom-width"
            onChange={(e) => {
              const width = parseInt(e.target.value);
              const height = parseInt((document.getElementById('custom-height') as HTMLInputElement)?.value || '600');
              if (width > 0) handleSizeChange(width, height);
            }}
          />
          <input
            type="number"
            placeholder="Height"
            id="custom-height"
            onChange={(e) => {
              const height = parseInt(e.target.value);
              const width = parseInt((document.getElementById('custom-width') as HTMLInputElement)?.value || '800');
              if (height > 0) handleSizeChange(width, height);
            }}
          />
        </div>
      </div>
    </div>
  );
}

Function Signature

setCanvasSize(width: number, height: number): void

Parameters:

  • width (number): The desired canvas width in pixels. Minimum value is 1.
  • height (number): The desired canvas height in pixels. Minimum value is 1.

Behavior:

  • Updates the canvas dimensions immediately
  • Ensures minimum dimensions of 1x1 pixel to prevent invalid canvas sizes
  • The change is persisted in the design state and included in exports
  • Elements on the canvas maintain their relative positions
  • The canvas is automatically re-rendered with the new dimensions

Use Cases:

  • Template switching with different aspect ratios
  • Responsive design tools
  • Custom size input forms
  • Batch resizing operations
  • Integration with external size selection tools

Advanced Usage with Types

import { 
  CanvasEditor, 
  CanvasDesign, 
  Config,
  exportToPNG,
  exportToJPG,
  exportToJSON,
  canvasToBlob,
  importFromJSON,
  importFromJSONData 
} from 'labellife-design-tool';
import type { CanvasElement } from 'labellife-design-tool';

const config: Config = {
  export: { png: true, jpg: true, json: true },
  multiPage: true,
  variables: true,
};

function MyEditor() {
  return <CanvasEditor name="Editor" config={config} />;
}

Template Import

The library provides two ways to import template designs:

File-Based Import

import { importFromJSON } from 'labellife-design-tool';

// Use with file input
function handleFileImport(event) {
  importFromJSON(
    event,
    (design) => {
      console.log('Design loaded:', design);
      // Use the design
    },
    (error) => {
      alert('Import failed: ' + error);
    }
  );
}

Direct JSON Data Import

import { importFromJSONData } from 'labellife-design-tool';

// Import JSON data directly (no file needed)
const templateData = {
  width: 800,
  height: 600,
  pages: [{
    id: "1",
    name: "Page 1",
    elements: [
      {
        type: "text",
        text: "Hello World",
        x: 100,
        y: 100,
        fontSize: 24
      }
    ],
    background: "white"
  }]
};

importFromJSONData(
  templateData,
  (design) => {
    console.log('Design loaded:', design);
    // Use the design in CanvasEditor
  },
  (error) => {
    console.error('Import failed:', error);
  }
);

Import from API

// Load template from API
async function loadTemplateFromAPI(templateId: string) {
  try {
    const response = await fetch(`/api/templates/${templateId}`);
    const templateData = await response.json();
    
    importFromJSONData(
      templateData,
      (design) => {
        // Template loaded successfully
        setDesign(design);
      },
      (error) => {
        alert('Failed to load template: ' + error);
      }
    );
  } catch (error) {
    console.error('API error:', error);
  }
}

Import with User Inputs

importFromJSONData(
  templateData,
  (design) => {
    // Design loaded with user inputs applied
    setDesign(design);
  },
  (error) => {
    alert('Import failed: ' + error);
  },
  (inputs, onComplete) => {
    // Show custom input modal
    showInputModal(inputs, (values) => {
      onComplete(values);
    });
  }
);

Comparison:

| Feature | importFromJSON | importFromJSONData | |---------|------------------|---------------------| | Input Source | File upload event | JSON data object | | File Reading | Built-in FileReader | Not needed | | Use Case | User file uploads | API data, database, programmatic imports | | Processing | Identical | Identical |

Simplified Template Loading (Recommended)

For the easiest template loading experience, we recommend using the new loadDesign method or loadTemplateFromJSON utility. These methods handle all the complexity internally and provide a clean, promise-based API.

Method 1: Using loadDesign with CanvasEditor Ref

This is the most straightforward approach - just pass the JSON data directly to the editor:

import { useRef } from 'react';
import { CanvasEditor, CanvasEditorRef } from 'labellife-design-tool';

function TemplateLoader() {
  const canvasEditorRef = useRef<CanvasEditorRef>(null);
  
  const loadTemplate = async (templateData: any) => {
    try {
      await canvasEditorRef.current?.loadDesign(templateData);
      console.log('Template loaded successfully!');
    } catch (error) {
      console.error('Failed to load template:', error);
      alert('Failed to load template: ' + error.message);
    }
  };
  
  // Example: Load template from API
  const loadTemplateFromAPI = async (templateId: string) => {
    try {
      const response = await fetch(`/api/templates/${templateId}`);
      const templateData = await response.json();
      
      await canvasEditorRef.current?.loadDesign(templateData);
      console.log('Template loaded successfully!');
    } catch (error) {
      console.error('API error:', error);
      alert('Failed to load template: ' + error.message);
    }
  };
  
  return (
    <div>
      <CanvasEditor ref={canvasEditorRef} name="Template Editor" />
      <button onClick={() => loadTemplateFromAPI('123')}>
        Load Template
      </button>
    </div>
  );
}

Method 2: Using loadTemplateFromJSON Utility

This provides a convenient wrapper with additional error checking:

import { useRef } from 'react';
import { CanvasEditor, CanvasEditorRef, loadTemplateFromJSON } from 'labellife-design-tool';

function TemplateLoader() {
  const canvasEditorRef = useRef<CanvasEditorRef>(null);
  
  const loadTemplate = async (templateData: any) => {
    try {
      await loadTemplateFromJSON(canvasEditorRef, templateData);
      console.log('Template loaded successfully!');
    } catch (error) {
      console.error('Failed to load template:', error);
    }
  };
  
  return <CanvasEditor ref={canvasEditorRef} name="Template Editor" />;
}

Your Colleague's Use Case

Here's how your colleague can now load templates with the simplified API:

const loadTemplate = async (id) => {
    try {
        const response = await fetch(`${window.wpDesignData.baseUrl}/api/polotno/get-template/${id}/`, {
            headers: { 'Authorization': `Bearer ${window.wpDesignData.userToken}` }
        });
        const templateData = await response.json();

        if (templateData) {
            const detailFileJson = templateData.details_file;
            const responseFile = await fetch(detailFileJson);
            const templateDataJson = await responseFile.json();
            
            // Simple one-line call - no callbacks needed!
            await canvasEditorRef.current.loadDesign(templateDataJson);
            console.log('Template loaded successfully');
        }
    } catch (error) {
        console.error('Failed to load template:', error);
        alert('Failed to load template: ' + error.message);
    }
};

Key Benefits:

  • No callbacks required - uses promises for cleaner async handling
  • Automatic error handling - throws descriptive errors
  • Internal state management - handles setDesign automatically
  • History integration - automatically saves to undo/redo history
  • Modal integration - uses existing TemplateInputModal for user inputs
  • TypeScript support - full type safety

What is canvasEditorRef?

canvasEditorRef is a React ref object that provides access to the CanvasEditor component's internal methods. Here's how to create and use it:

import { useRef } from 'react';
import { CanvasEditor, CanvasEditorRef } from 'labellife-design-tool';

function MyComponent() {
  // 1. Create the ref with proper typing
  const canvasEditorRef = useRef<CanvasEditorRef>(null);
  
  // 2. Pass it to the CanvasEditor component
  return (
    <CanvasEditor 
      ref={canvasEditorRef}
      name="My Editor"
      config={{ /* your config */ }}
    />
  );
}

Important Notes:

  • The ref is null until the component mounts
  • Always check canvasEditorRef.current before using it
  • The ref provides access to methods like loadDesign, exportToPNG, etc.
  • TypeScript provides full autocomplete and type checking

Function Signature

loadDesign(jsonData: any): Promise<void>

Parameters:

  • jsonData (any): The raw JSON template data to load

Returns:

  • Promise<void>: Resolves when the design is loaded, rejects on error

Behavior:

  • Converts template format to internal CanvasDesign format
  • Shows the existing TemplateInputModal for required user inputs
  • Automatically handles optional inputs with empty values
  • Updates the editor state
  • Saves to undo/redo history
  • Throws descriptive errors for invalid data or user cancellation
  • Supports both required and optional user input fields

Canvas to Blob Export

The library provides a flexible canvasToBlob function that allows converting the canvas to a Blob for various use cases:

import { canvasToBlob } from 'labellife-design-tool';

// Example usage in a custom component
const handleExport = async () => {
  if (stageRef.current) {
    try {
      // Get canvas as PNG blob
      const blob = await canvasToBlob(stageRef.current, 'png', { pixelRatio: 2 });
      
      // Example: Convert to base64
      const reader = new FileReader();
      reader.readAsDataURL(blob);
      reader.onloadend = () => {
        const base64data = reader.result;
        console.log('Base64:', base64data);
        
        // Use base64 data with APIs, embed in other applications, etc.
      };
      
      // Example: Create File object for upload
      const file = new File([blob], 'canvas-export.png', { type: 'image/png' });
      
      // Example: Create object URL for display
      const blobUrl = URL.createObjectURL(blob);
      // Remember to revoke URL when done
      // URL.revokeObjectURL(blobUrl);
      
    } catch (error) {
      console.error('Error exporting canvas:', error);
    }
  }
};

The canvasToBlob function parameters:

  • stage: The Konva.Stage object to convert
  • format: Output format ('png' or 'jpg')
  • options: Additional options object
    • quality: Quality for JPG format (0-1)
    • pixelRatio: Resolution multiplier (default: 2)

Navbar Customization

You can fully customize the navbar by configuring the navbar property in the config object:

import { CanvasEditor } from 'labellife-design-tool';

function App() {
  return (
    <CanvasEditor
      name="My Design Tool"
      config={{
        // Other configuration options...
        navbar: {
          // Control visibility of default sections
          showAppName: true, // Show/hide the app name on the left
          showHistoryControls: true, // Show/hide undo/redo controls
          showZoomControls: true, // Show/hide zoom controls
          
          // Override default sections
          defaultSections: {
            appName: {
              label: "My Custom Tool Name", // Change the default app name
            }
          },
          
          // Add custom sections to the navbar
          customSections: [
            {
              id: "saveButton",
              type: "custom",
              position: "right", // 'left', 'center', or 'right'
              label: "Save",
              icon: "💾", // Optional icon
              onClick: () => saveProject(),
              order: 5 // Controls the ordering of sections (lower numbers come first)
            },
            {
              id: "helpLink",
              type: "custom",
              position: "right",
              label: "Help",
              icon: "❓",
              onClick: () => showHelpModal(),
              order: 40
            },
            // For complex custom sections, you can provide a React component
            {
              id: "customDropdown",
              type: "custom",
              position: "right",
              content: <CustomDropdownComponent />,
              order: 50
            }
          ]
        }
      }}
    />
  );
}

WordPress Integration

The library includes a special WordPress-compatible entry point that ensures proper compatibility with WordPress's React environment.

Using with WordPress

When using this library in a WordPress environment, import from the WordPress-specific entry point:

// Import from the WordPress-specific entry point
import { CanvasEditor } from 'labellife-design-tool/wordpress';
import 'labellife-design-tool/styles'; // Import CSS

// Use as normal
function MyWordPressComponent() {
  return (
    <CanvasEditor
      name="WordPress Design Editor"
      config={{
        export: { png: true, jpg: true, json: true },
        multiPage: true
      }}
    />
  );
}

WordPress Helper Function

For simpler integration with vanilla WordPress plugins, a helper function is provided:

import { initWordPressCanvasEditor } from 'labellife-design-tool/wordpress';
import 'labellife-design-tool/styles'; // Import CSS

// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
  const containerId = 'editor-container'; // ID of your container element
  
  initWordPressCanvasEditor(containerId, {
    name: "WordPress Editor",
    config: {
      export: { png: true, jpg: true, json: true },
      multiPage: true,
      variables: true
    }
  });
});

How the WordPress Compatibility Works

The WordPress integration automatically handles compatibility with WordPress's React environment by:

  1. Detecting when React is available globally (as in WordPress)
  2. Adding necessary JSX runtime compatibility functions
  3. Making the library work seamlessly in WordPress's React environment

Project Structure

  • src/lib/index.ts - Library entry point (npm package)
  • src/wordpress.tsx - WordPress-specific entry point
  • src/CanvasEditor.tsx - Main editor component
  • src/types/ - TypeScript type definitions
  • src/components/ - React components
  • src/panels/ - Sidebar panels
  • src/elements/ - Canvas element components
  • src/utils/ - Utility functions

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.