@ninesstudios/whiteboard
v0.2.2
Published
Whiteboard drawing application
Readme
Whiteboard
A tailored React Whiteboard component that supports drawing, shapes, text, images, and more. It comes with built-in state management and a customizable toolbar.
Features
- Drawing Tools: Pencil, Text, Rectangle, Circle, Triangle, Diamond, Star, Heart, Hexagon, Octagon, Arrow.
- Image Support: Insert and manipulate images on the canvas.
- Canvas Controls: Pan and Zoom functionality.
- Grid System: Toggleable background grid.
- Selection: Select, move, and modify elements.
- Export: Export the whiteboard content as a PNG image.
- Locking Mechanism: Set the whiteboard to read-only mode with custom messages.
- Action Handling: Hook into internal actions for external syncing or logging.
Installation
Install the package and its peer dependencies:
npm install whiteboard react react-domNote: If this package is not yet published to npm, you can install it locally or link it.
Usage
Basic Usage
Import the Whiteboard component and the stylesheet. The component takes up 100% of the parent container's width and height.
import React from 'react';
import Whiteboard from 'whiteboard';
import 'whiteboard/style.css'; // Import styles
function App() {
return (
<div style={{ width: '100vw', height: '100vh', border: '1px solid #ccc' }}>
<Whiteboard />
</div>
);
}
export default App;Advanced Usage
You can control the whiteboard state and handle events using props and a ref.
import React, { useRef } from 'react';
import Whiteboard from 'whiteboard';
import 'whiteboard/style.css';
function App() {
const whiteboardRef = useRef(null);
const handleAction = (action) => {
console.log('Action occurred:', action);
// Send action to server for real-time collaboration
};
const handleLockChange = (isLocked) => {
console.log('Lock state changed:', isLocked);
};
return (
<div style={{ width: '100vw', height: '100vh' }}>
<Whiteboard
ref={whiteboardRef}
onAction={handleAction}
isLocked={false}
lockText="This board is currently locked"
onLockChange={handleLockChange}
/>
</div>
);
}
// Example: read & replace elements
// const elements = whiteboardRef.current?.getElements();
// whiteboardRef.current?.setElements(elements);
// Example: clear all elements
// whiteboardRef.current?.clearElements();Programmatic Updates (applyAction)
You can programmatically apply actions to the whiteboard using the applyAction method exposed via the ref. This is essential for implementing real-time collaboration where you need to apply actions received from a server.
import React, { useEffect, useRef } from 'react';
import Whiteboard from 'whiteboard';
function App() {
const whiteboardRef = useRef(null);
useEffect(() => {
// Example: mimicking receiving an action from a websocket
const mockIncomingAction = {
type: 'whiteboard/addElement',
payload: {
id: 'remote-1',
type: 'rectangle',
x: 200,
y: 200,
width: 100,
height: 100,
fillColor: '#FF0000'
}
};
// Apply the action to the whiteboard
if (whiteboardRef.current) {
// In a real app, this would be inside a socket event listener
setTimeout(() => {
whiteboardRef.current.applyAction(mockIncomingAction);
}, 1000);
}
}, []);
return (
<div style={{ width: '100vw', height: '100vh' }}>
<Whiteboard ref={whiteboardRef} />
</div>
);
}API Reference
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| onAction | (action: object) => void | undefined | Callback fired for content-changing actions (e.g., add/remove/move elements, text edits). It does not emit high-frequency UI actions like pan/zoom/selection/tool changes. |
| isLocked | boolean | false | When true, disables all editing tools and shows a lock indicator. |
| lockText | string | undefined | Custom text to display on the lock indicator when isLocked is true. |
| onLockChange | (isLocked: boolean) => void | undefined | Callback fired when the lock state changes. |
| onImageUpload | (file: File) => Promise<string> | undefined | Async callback to handle image uploads. Must return a Promise that resolves to the image URL. |
Handling Image Uploads
By default, the whiteboard creates temporary Blob URLs for uploaded images. This works great for local sessions but won't work for real-time collaboration because Blob URLs are local to the browser tab.
To support multiplayer image sharing, you must provide the onImageUpload prop. This function should upload the file to your server (e.g., S3, Cloudinary) and return the public URL.
const handleImageUpload = async (file) => {
// 1. Upload file to your server
const formData = new FormData();
formData.append('file', file);
const response = await fetch('https://your-api.com/upload', {
method: 'POST',
body: formData
});
const data = await response.json();
// 2. Return the public URL
return data.url;
};
<Whiteboard onImageUpload={handleImageUpload} />Ref Methods
You can access these methods by passing a ref to the Whiteboard component.
| Method | Arguments | Description |
|--------|-----------|-------------|
| applyAction | (action: object) | Dispatches an action to the internal Redux store. Useful for applying remote updates. |
| getElems / getElements | () | Returns the current elements array from the internal store. |
| setElems / setElements | (elements: object[]) | Replaces the entire elements array in the internal store. |
| clearElems / clearElements | () | Clears all elements from the internal store. |
| canvas | - | Returns the underlying HTMLCanvasElement. |
Redux Actions Helper
For convenience (e.g., building your own applyAction payloads), the package exports the internal Redux action creators:
import { whiteboardActions } from 'whiteboard';
// Example: build an action object to send/apply
const action = whiteboardActions.clearElements();
// action.type === 'whiteboard/clearElements'All available actions
| Action creator | action.type | Payload |
|---|---|---|
| toggleGrid() | whiteboard/toggleGrid | none |
| setGridSize(size) | whiteboard/setGridSize | number |
| setGridColor(color) | whiteboard/setGridColor | string |
| setSelectedTool(tool) | whiteboard/setSelectedTool | string |
| setToolProperty({ tool, property, value }) | whiteboard/setToolProperty | { tool: string, property: string, value: any } |
| addElement(element) | whiteboard/addElement | Element (see “addElement payload” below) |
| updateElement({ id, updates }) | whiteboard/updateElement | { id: string, updates: object } |
| removeElement(id) | whiteboard/removeElement | string |
| setSelectedElement(idOrNull) | whiteboard/setSelectedElement | string \| null |
| clearElements() | whiteboard/clearElements | none |
| setElements(elements) | whiteboard/setElements | object[] |
| setPan({ x, y }) | whiteboard/setPan | { x: number, y: number } |
| setZoom(zoom) | whiteboard/setZoom | number |
| resetViewport() | whiteboard/resetViewport | none |
| fitToView({ canvasWidth, canvasHeight, padding? }) | whiteboard/fitToView | { canvasWidth: number, canvasHeight: number, padding?: number } |
| bringToFront(id) | whiteboard/bringToFront | string |
| sendToBack(id) | whiteboard/sendToBack | string |
| setLocked(isLocked) | whiteboard/setLocked | boolean |
| setWatermarkEnabled(visible) | whiteboard/setWatermarkEnabled | boolean |
| setWatermarkText(text) | whiteboard/setWatermarkText | string |
addElement(element) payload
addElement expects a full element object. Coordinates are world coordinates (the board handles pan/zoom internally).
Common fields (all element types):
| Field | Type | Notes |
|---|---|---|
| id | string | Must be unique (commonly Date.now().toString()). |
| type | string | One of the supported element types below. |
Element schemas by type:
rectangle / triangle / diamond / hexagon / octagon
- Required:
x: number,y: number,width: number,height: number - Styling:
strokeColor: string,fillColor: string,lineWidth: number
- Required:
circle
- Required:
centerX: number,centerY: number,radius: number - Styling:
strokeColor: string,fillColor: string,lineWidth: number
- Required:
pencil
- Required:
points: Array<{ x: number, y: number }> - Styling:
strokeColor: string,lineWidth: number
- Required:
star / heart
- Required:
centerX: number,centerY: number,size: number - Styling:
strokeColor: string,fillColor: string,lineWidth: number
- Required:
arrow
- Required:
startX: number,startY: number,endX: number,endY: number - Styling:
strokeColor: string,fillColor: string,lineWidth: number
- Required:
text
- Required:
x: number,y: number,text: string - Styling:
fontSize: number,fontColor: string
- Required:
image
- Required:
x: number,y: number,width: number,height: number,src: string - Notes:
srcis typically a Data URL (fromFileReader.readAsDataURL).
- Required:
Example (rectangle):
import { whiteboardActions } from 'whiteboard';
const action = whiteboardActions.addElement({
id: '1',
type: 'rectangle',
x: 100,
y: 80,
width: 200,
height: 120,
strokeColor: '#000000',
fillColor: '#ffffff',
lineWidth: 2,
});License
MIT
